Jeison Yehuda Amihud (Blender Dumbass)
162d3f8038
Implemented missing features that could be seen as bugs for the casual users. Such as sync of the main checklist. And other minor changes.
643 lines
29 KiB
Python
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)
|