# 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)