Blender-Pipeline/studio/history.py
Jeison Yehuda Amihud (Blender Dumbass) 162d3f8038 Multiuser Bug Fixes
Implemented missing features that could be
seen as bugs for the casual users.

Such as sync of the main checklist. And other
minor changes.
2021-01-04 15:52:29 +00:00

643 lines
29 KiB
Python

# THIS FILE IS A PART OF VCStudio
# PYTHON 3
import os
import datetime
import threading
# GTK module ( Graphical interface
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import Gdk
import cairo
# Own modules
from settings import settings
from settings import talk
from project_manager import pm_project
from studio import analytics
from studio import studio_nodes
from studio import studio_dialogs
from studio import story
from studio import analytics
from studio import checklist
#UI modules
from UI import UI_elements
from UI import UI_color
################################################################################
# This file is going to hande HISTORY. Both recoring and showing. I suppose in
# future when I gonna implement Networking Multiuser support. History will be
# handling some of it too. Will see. ( I might forget about this comment and it's
# already implemented, who knows. )
################################################################################
def record(win, filename, task, checklist=[] ):
############################################################################
# This function will write the history file. Or incase connecting to multiuser
# will also notify the server of a task being done. This file probably going
# to change quite usually. Because I might start writting in more or less
# stuff. So keep an eye on it.
# Now since this will touch the multiuser and I can't guarantee that all
# users will have the same version. The multiuser part of this should insure
# stable backward compatibility with all verisons of VCStudio.
# BUT THERE IS NO WARRANTY! So who knows if exidentaly break something for
# the old versions. So keeping up to date quite advisable. Or at least keep
# the same version of VCStudio across all the computers involved in multiuser.
############################################################################
################### CHECKLIST OF IMPEMENTED DATATYPES ######################
# [V] [Openned] # WHEN BLEND FILE IS OPENNED [ settings/oscalls.py ]
# [V] [Linked] # AS LINKED ASSET INTO BLEND [ studio/studio_shot_linkLayer.py ]
# [V] [Edited] # WHEN EDITED STORY [ studio/studio_scriptLayer.py ]
# [V] [Updated] # WHEN CONFIGURED STUFF [ studio/studio_asset_configureLayer.py ]
# [V] [Added] # WHEN ADDING A BLEND FILE [ settings/oscalls.py ]
# [V] [Added Asset] # WHEN ADDING AN ASSET [ studio/studio_asset_selectLayer.py ]
# [V] [Checked] # WHEN [V] IN CHECKLIST [ studio/checklist.py ]
# [V] [Un-Checked] # WHEN [ ] IN CHECKLIST [ studio/checklist.py ]
############################################################################
############### PARSING THE URLS ##############
print("HISTORY-RAW: ", filename, task, checklist)
# Fisrt of all let's remove the project's url
filename = filename.replace(win.project, "").replace("//", "/")
# Now let's find a type
t = "files"
item = filename
if filename.startswith("/rnd"):
t = "scenes"
item = filename.replace("/rnd", "")
filename = filename[filename.rfind("/"):]
item = item[:item.rfind(filename)]
elif filename.startswith("/ast") or filename.startswith("/dev"):
t = "assets"
if filename.startswith("/ast"):
filename = "[asset_blend]"
item = item.replace(".blend", "").replace("/ast", "")
elif filename.startswith("/dev"):
f = filename[filename.rfind("/"):]
item = filename.replace(f, "").replace("/dev", "")
filename = f
else:
filename = filename[filename.rfind("/"):]
item = item.replace("//", "/")
filename = filename.replace("//", "/")
# Somebody please look at this. Maybe there is more clever way to record
# editing of scenes. I don't know.
if task == "[Edited]":
item = filename
print("HISTORY: ", t, item, filename, task, checklist)
########### WRITING TO THE ANANLYTICS ###########
new_date_format = "%Y/%m/%d"
date = datetime.datetime.strftime(datetime.datetime.today(), new_date_format)
seconds_format = "%H:%M:%S"
time = datetime.datetime.strftime(datetime.datetime.now(), seconds_format)
username = win.settings["Username"]
# Now since we have our DATE and TIME we can start writing.
if date not in win.analytics["dates"]:
win.analytics["dates"][date] = {}
if t not in win.analytics["dates"][date]:
win.analytics["dates"][date][t] = {}
if item not in win.analytics["dates"][date][t]:
win.analytics["dates"][date][t][item] = []
# APPENDNING
if not checklist:
win.analytics["dates"][date][t][item].append(
[
time,
"history",
filename,
task,
username
]
)
else:
win.analytics["dates"][date][t][item].append(
[
time,
"history",
filename,
task,
checklist,
username
]
)
# REFRASHING
analytics.save(win.project, win.analytics)
win.analytics = analytics.load(win.project)
# Multiuser sycning
win.multiuser["request"] = "analytics"
def draw(outlayer, win):
x = 10
y = 70
width = win.current["w"] / 4 - 20
height = win.current["h"] - 80
# Now let's make a layer.
# Making the layer
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
layer = cairo.Context(surface)
layer.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
# Clip
UI_elements.roundrect(layer, win,
0,
0,
width,
height,
10,
fill=False)
layer.clip()
# Background
#UI_color.set(layer, win, "dark_overdrop")
#layer.rectangle(0,0,width, height)
#layer.fill()
new_date_format = "%Y/%m/%d"
today = datetime.datetime.strftime(datetime.datetime.today(), new_date_format)
if win.cur == "/set" :
UI_elements.text(outlayer, win, "current_date_setting",
width-100-(width-6*40),
15,
(width-6*40),
40,
set_text=win.current["date"])
if win.text["current_date_setting"]["text"] != win.current["date"]\
and analytics.ifdate(win.text["current_date_setting"]["text"]):
def do():
win.current["date"] = win.text["current_date_setting"]["text"]
win.textactive = ""
UI_elements.roundrect(outlayer, win,
width-140,
15,
40,
40,
10,
button=do,
icon="ok",
tip=talk.text("checked"))
elif win.current["date"] != today:
def do():
win.current["date"] = today
win.text["current_date_setting"]["text"] = today
win.textactive = ""
UI_elements.roundrect(outlayer, win,
width-140,
15,
40,
40,
10,
button=do,
icon="cancel",
tip=talk.text("cancel"))
if "history" not in win.scroll:
win.scroll["history"] = 0
current_Y = 0
# OKAY I GUESS SINCE HISTORY IS RELATIVELLY SIMPLE (LOL) I WILL PARSE IT
# STRAIGHT. Without making a whole lot of parsing. Because with schedules
# it resulted in some rather hilarious stuff.
if "history_selection" not in win.current:
win.current["history_selection"] = {
"date":win.current["date"],
"item":False,
"user":False
}
dates = win.analytics["dates"]
for date in dates:
if (not win.cur or win.cur == "/set" )and date != win.current["date"]:
continue
theday = date
date = dates[date]
# Now let's parse throught it. I guess.
for i in ["files", "assets", "scenes"]:
if i in date:
for item in date[i]:
# This is our individual items. I want to create a folder
# with USERS inside it. So you could see who done what.
if win.cur.count("/") > 1:
tmp = win.cur.replace("/","",1).split("/")
acur, name = tmp[0], tmp[1]
else:
name = win.cur[win.cur.rfind("/")+1:]
acur = ""
if win.cur not in item and win.cur != "/set":
continue
name = item[item.rfind("/")+1:]
acur = item.replace(name, "").replace("/", "")
found = {}
for stuff in date[i][item]:
if stuff[1] == "history":
if stuff[-1] not in found:
found[stuff[-1]] = []
if stuff[3] in ["[Checked]", "[Un-Checked]"]:
found[stuff[-1]].append([stuff[0],stuff[2],stuff[3],stuff[4]])
else:
found[stuff[-1]].append([stuff[0],stuff[2],stuff[3],[]])
if found:
# Now let's output our findings
UI_color.set(layer, win, "node_background")
UI_elements.roundrect(layer, win,
0,
win.scroll["history"] + current_Y,
width,
50,
10)
# ICON
if i == "scenes" and win.cur == "/set":
if item.count("/") > 1:
UI_elements.image(layer, win,
"settings/themes/"+win.settings["Theme"]+"/icons/shot.png",
5, win.scroll["history"] + current_Y+5, 40, 40)
else:
UI_elements.image(layer, win,
"settings/themes/"+win.settings["Theme"]+"/icons/scene.png",
5, win.scroll["history"] + current_Y+5, 40, 40)
elif i == "assets" and win.cur == "/set":
if os.path.exists(os.getcwd()+"/settings/themes/"+win.settings["Theme"]+"/icons/"+acur+".png"):
UI_elements.image(layer, win,
"settings/themes/"+win.settings["Theme"]+"/icons/"+acur+".png",
5, win.scroll["history"] + current_Y+5, 40, 40)
else:
UI_elements.image(layer, win,
"settings/themes/"+win.settings["Theme"]+"/icons/"+name+".png",
5, win.scroll["history"] + current_Y+5, 40, 40)
name = talk.text(name.replace("/", ""))
else:
UI_elements.image(layer, win,
"settings/themes/"+win.settings["Theme"]+"/icons/history.png",
5, win.scroll["history"] + current_Y+5, 40, 40)
# MAIN TASK
UI_color.set(layer, win, "text_normal")
layer.set_font_size(20)
layer.move_to(
50,
win.scroll["history"] + current_Y+30,
)
if win.cur == "/set":
if "chr" not in item and "veh" not in item and "loc" not in item and "obj" not in item and "set" not in item:
layer.show_text(item.replace("/","",1).replace("/", " | "))
else:
layer.show_text(name.replace("project.progress", win.analytics["name"]))
else:
layer.show_text(theday)
# Selection button
def do():
win.current["history_selection"]["item"] = item
win.current["history_selection"]["date"] = theday
win.current["history_selection"]["user"] = False
UI_elements.roundrect(layer, win,
0,
win.scroll["history"] + current_Y,
width,
50,
10,
button=do,
fill=False,
offset=[x,y])
layer.stroke()
if win.current["history_selection"]["item"] == item\
and win.current["history_selection"]["date"] == theday:
UI_color.set(layer, win, "progress_background")
UI_elements.roundrect(layer, win,
0,
win.scroll["history"] + current_Y,
width,
50,
10,
fill=False)
layer.stroke()
current_Y = current_Y + 60
# It a history is selected you could open up the list
# of users that worked on a given task.
for user in found:
UI_color.set(layer, win, "node_background")
UI_elements.roundrect(layer, win,
20,
win.scroll["history"] + current_Y,
width-20,
50,
10)
UI_elements.image(layer, win,
"settings/themes/"+win.settings["Theme"]+"/icons/user.png",
25, win.scroll["history"] + current_Y+5, 40, 40)
# NAME OF THE USER
UI_color.set(layer, win, "text_normal")
layer.set_font_size(20)
layer.move_to(
70,
win.scroll["history"] + current_Y+30,
)
layer.show_text(user)
# SELECTION BOX
def do():
win.current["history_selection"]["user"] = user
UI_elements.roundrect(layer, win,
20,
win.scroll["history"] + current_Y,
width-20,
50,
10,
button=do,
fill=False,
offset=[x,y])
layer.stroke()
# IF THE USER IS SELECTED
if win.current["history_selection"]["user"] == user:
UI_color.set(layer, win, "progress_background")
UI_elements.roundrect(layer, win,
20,
win.scroll["history"] + current_Y,
width-20,
50,
10,
fill=False)
layer.stroke()
current_Y = current_Y + 60
for stuff in found[user]:
UI_color.set(layer, win, "node_background")
UI_elements.roundrect(layer, win,
40,
win.scroll["history"] + current_Y,
width-40,
50,
10)
# Here if both category and the user are
# selected I need to parse the data of
# the history to present it to you.
operation_icons = {
"[Openned]":"blender",
"[Linked]":"file_link",
"[Edited]":"scene",
"[Updated]":"update",
"[Added]":"new",
"[Added Asset]":"asset_new",
"[Checked]":"checked",
"[Un-Checked]":"unchecked"
}
try:
icon = operation_icons[stuff[2]]
except:
icon = "history"
UI_elements.image(layer, win,
"settings/themes/"+win.settings["Theme"]+"/icons/"+icon+".png",
45, win.scroll["history"] + current_Y+5, 40, 40)
# Next will be to add some text about
# what exactly was done here. And I know
# that we are already super inside. I
# mean indentation. If somebody want's
# to reduse a few spaces. They are
# welcome to try.
# Let's split the the history entries to
# 2 types. Checklists and everything else.
# CHECKLISTS
if "Checked" in stuff[2]:
# Let's get task and url
try:
task = stuff[-1][-1]
fullurl = ""
for e in stuff[-1][:-1]:
fullurl = fullurl+e+" > "
except:
tasl = ""
fullurl = ""
# MAIN TASK
UI_color.set(layer, win, "text_normal")
layer.set_font_size(20)
layer.move_to(
80,
win.scroll["history"] + current_Y+24,
)
layer.show_text(task)
# TASK URL INSIDE THE CHECKLIST
if fullurl:
UI_color.set(layer, win, "text_normal")
layer.set_font_size(10)
layer.move_to(
90+len(task)*12,
win.scroll["history"] + current_Y+24,
)
layer.show_text(fullurl)
else:
# If it's not a checklist. I just want
# to output what it is and what action
# happened to it.
task = stuff[1].replace("/", " ")
if task == "[asset_blend]":
task = " Asset Blend File"
# WHAT
UI_color.set(layer, win, "text_normal")
layer.set_font_size(20)
layer.move_to(
80,
win.scroll["history"] + current_Y+24,
)
layer.show_text(task)
# WHAT WAS DONE
UI_color.set(layer, win, "text_normal")
layer.set_font_size(10)
layer.move_to(
90+len(task)*12,
win.scroll["history"] + current_Y+24,
)
layer.show_text(stuff[2])
UI_color.set(layer, win, "text_normal")
layer.set_font_size(15)
layer.move_to(
92,
win.scroll["history"] + current_Y+41,
)
layer.show_text(stuff[0])
# Okay and the last thing. The button of
# when it's clicked. For example to get
# to the item. Or to find the task in
# the checklist. For what ever reason.
def do():
# Let's make the auto-selecting of
# the checklists. OMG I really have
# no space left to type. OMG. What
# is this.
if "Checked" in stuff[2]:
# AAAAAAAAAAAAAAAAAAAAAAAAAAAA
# How am I supposed to tell thi-
# ngs? OMG.
if "schedule_task_selected" not in win.current:
win.current["schedule_task_selected"] = []
win.current["schedule_task_selected"] = \
[[[stuff[0], "schedule", stuff[1], stuff[2], stuff[3]], 0], item]
# Now since we did this weird
# what ever the mack. Let's make so
# it's going to the asset.
if i == "assets":
goto = "assets"
elif i == "scenes":
goto = "script"
else:
goto = "analytics"
win.url = goto
win.cur = item
win.current["asset_left_panel"] = "history"
if stuff[2] != "[Added Asset]":
UI_elements.roundrect(layer, win,
40,
win.scroll["history"] + current_Y,
width-40,
50,
10,
button=do,
fill=False,
offset=[x,y])
layer.stroke()
current_Y = current_Y + 60
else:
current_Y = current_Y + 60
else:
current_Y = current_Y + 60
# Outputting the layer
outlayer.set_source_surface(surface, x, y)
outlayer.paint()
# Scroll
UI_elements.scroll_area(outlayer, win, "history",
x+0,
y+50,
width,
height-50,
current_Y,
bar=True,
mmb=True)