Blender-Pipeline/studio/schedule.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

820 lines
31 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 handle SCHEDULING. Primarily the output of the schedules
# to the screen. But also probably a lot of other things related to schediles.
################################################################################
def filter(project, dates):
############################################################################
# This function is called on reading of analytics read. It will check whether
# schedules are finished. Or if they are even found. Using studio/checklist.py
# as a help.
############################################################################
checkcashe = {} # Temporary checklist cashe.
delete = [] # Stuff I want to delete later
for date in dates:
for i in ["assets", "scenes", "files"]:
if i in dates[date]:
for url in dates[date][i]:
for num, entry in enumerate(dates[date][i][url]):
if dates[date][i][url][num][1] == "schedule":
# First of all. I didn't think about it when
# writting the analytics read
# that I will need to know whether schedules
# are [Checked] or [Un-Checked]
if not "[Checked]" in dates[date][i][url][num]\
and not "[Un-Checked]" in dates[date][i][url][num]:
dates[date][i][url][num].insert(3, "[Un-Checked]")
# Then let's actually try to find whether the task
# is checked.
# For now let's do in a simple way. Because
# if the whole checklist is 100% done. Then
# obviously the task is also checked.
path = dates[date][i][url][num][2]
try:
# Let's check if path exists in the project first.
if os.path.exists(project+"/dev"+url+path):
path = project+"/dev"+url+path
elif os.path.exists(project+"/rnd"+url+path):
path = project+"/rnd"+url+path
elif os.path.exists(project+"/set/"+path):
path = project+"/set/"+path
elif os.path.exists(project+"/"+path):
path = project+"/"+path
if path not in checkcashe:
checkcashe[path] = checklist.get_list(path)
# Here we check whether the checklist is 100%
if checkcashe[path]["fraction"] == 1.0:
dates[date][i][url][num][3] = "[Checked]"
# But what if it's not? Now we need to check
# them 1 by one.
else:
dates[date][i][url][num][3] = "[Un-Checked]"
task = checklist.get_task_by_path(checkcashe[path]["subtasks"],dates[date][i][url][num][4])
# Now if the task is not found. We delete the schedule
if task:
if task["fraction"] == 1.0:
dates[date][i][url][num][3] = "[Checked]"
else:
delete.append([date,i,url,dates[date][i][url][num]])
except:
delete.append([date,i,url,dates[date][i][url][num]])
# Deleting all kinds of buddah
for i in delete:
q,w,e,r = i
if r in dates[q][w][e]:
dates[q][w][e].remove(r)
return dates
def get_schedules(dates):
############################################################################
# This function will parse the dates data from the analytics. And get only
# the schedules. In the format {date {url, [list of schedules]}}
############################################################################
newdates = {}
for date in dates:
if date not in newdates:
newdates[date] = {}
for i in ["assets", "scenes", "files"]:
if i in dates[date]:
for url in dates[date][i]:
for num, entry in enumerate(dates[date][i][url]):
if entry[1] == "schedule":
if url not in newdates[date]:
newdates[date][url] = []
if [entry, num] not in newdates[date][url]:
newdates[date][url].append([entry, num])
return newdates
def draw(outlayer, win):
x = 10
y = 70
width = win.current["w"] / 4 - 20
height = win.current["h"] - 80
# At the top above the schedules. In the bar. i want to put 2
# buttons. One will hide done schedules, the other will
# hide everything scheduled for other users.
if "schedule_layer_settings" not in win.current:
win.current["schedule_layer_settings"] = {
"checked":False,
"multiuser":False
}
for num, button in enumerate(win.current["schedule_layer_settings"]):
if win.current["schedule_layer_settings"][button]:
UI_color.set(outlayer, win, "progress_time")
UI_elements.roundrect(outlayer, win,
20+width-(40*num)-60,
15,
40,
40,
10)
def do():
win.current["schedule_layer_settings"][button] = not win.current["schedule_layer_settings"][button]
UI_elements.roundrect(outlayer, win,
20+width-(40*num)-60,
15,
40,
40,
10,
do,
button)
# 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()
# Getting various screadule data
schedules = get_schedules(win.analytics["dates"])
new_date_format = "%Y/%m/%d"
today = datetime.datetime.strftime(datetime.datetime.today(), new_date_format)
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"))
slist = []
for date in schedules:
if win.current["date"] != today and date != win.current["date"]:
continue
for item in schedules[date]:
if win.cur in item or not win.cur or win.cur == "/set":
slist.append([date, item, schedules[date][item]])
# Selection magic
if "schedule_task_selected" not in win.current:
win.current["schedule_task_selected"] = False
# Let's draw them to the screen in some way
if "schedule" not in win.scroll:
win.scroll["schedule"] = 0
current_Y = 0
for entry in slist:
for thing in entry[2]:
# Parsing the cur to get name and type
name = entry[1][entry[1].rfind("/")+1:]
acur = entry[1].replace(name, "").replace("/", "")
if entry[1].count("/") > 1:
tmp = entry[1].replace("/","",1).split("/")
acur, name = tmp[0], tmp[1]
else:
name = entry[1][entry[1].rfind("/")+1:]
acur = ""
fullurl = ""
for e in thing[0][4][:-1]:
fullurl = fullurl+e+" > "
if acur in ["chr", "veh", "loc","obj"]:
itemtype = "assets"
elif not acur:
itemtype = "files"
else:
itemtype = "scenes"
try:
if not win.analytics["dates"][entry[0]][itemtype]\
[entry[1]]:
continue
except:
continue
UI_color.set(layer, win, "node_background")
# If not all users show only current user's tasks
if not win.current["schedule_layer_settings"]["multiuser"]\
and thing[0][-1] != win.settings["Username"]:
continue
# If the task is checked
if thing[0][3] == "[Checked]":
if not win.current["schedule_layer_settings"]["checked"]:
continue
UI_color.set(layer, win, "node_blendfile") # The Green
elif entry[0] != "1997/07/30":
if entry[0] < today:
UI_color.set(layer, win, "node_badfile") # The Red
elif entry[0] > today:
UI_color.set(layer, win, "node_asset") # The Purple
UI_elements.roundrect(layer, win,
0,
win.scroll["schedule"] + current_Y,
width,
75,
10)
# Selection button
def do():
if win.current["tool"] == "selection":
if win.current["schedule_task_selected"] != [ thing, entry[1] ]:
win.current["schedule_task_selected"] = [ thing, entry[1] ]
else:
win.current["schedule_task_selected"] = False
# Clearing the text
try:
del win.text["schedule_username_setting"]
del win.text["schedule_date_setting"]
del win.text["schedule_time_setting"]
except:
pass
UI_elements.roundrect(layer, win,
0,
win.scroll["schedule"] + current_Y,
width,
75,
10,
button=do,
tip=entry[1]+" : "+fullurl+thing[0][4][-1],
offset=[x,y],
fill=False)
layer.stroke()
############# GRABBING FOR RE-SCHEDULING ##############
# Grab
if win.current["LMB"]\
and int(win.current["LMB"][0]) in range(int(x), int(x+width))\
and int(win.current["LMB"][1]) in range(int(y+win.scroll["schedule"] + current_Y), int(y+win.scroll["schedule"] + current_Y+75))\
and win.current["tool"] == "selection"\
and int(win.current["LMB"][0]) not in range(int(win.current["mx"]-2), int(win.current["mx"]+2))\
and int(win.current["LMB"][1]) not in range(int(win.current["my"]-2), int(win.current["my"]+2)):
try:
pop = win.analytics["dates"][entry[0]][itemtype]\
[entry[1]].pop(thing[1])
dev = ""
if itemtype == "assets":
dev = "/dev"
if itemtype == "scenes":
dev = "/rnd"
win.current["tool"] = "schedule"
win.current["grab_data"] = [dev+entry[1]+thing[0][2], win.url, entry[1], pop[4], pop[-1]]
win.url = "analytics"
except:
pass
# If you leave it here.
if win.current["tool"] == "schedule" and not win.current["LMB"]:
path, back, cur, schedulepath, username = win.current["grab_data"].copy()
path = path.replace(win.project, "")
path = path[path.find(cur)+len(cur):]
tname = cur[cur.rfind("/")+1:]
tacur = cur.replace(name, "").replace("/", "")
if tacur in ["chr", "veh", "loc","obj"]:
itemtype = "assets"
elif not tacur:
itemtype = "files"
else:
itemtype = "scenes"
theday = win.current["date"]
if theday not in win.analytics["dates"]:
win.analytics["dates"][theday] = {}
if itemtype not in win.analytics["dates"][theday]:
win.analytics["dates"][theday][itemtype] = {}
if cur not in win.analytics["dates"][theday][itemtype]:
win.analytics["dates"][theday][itemtype][cur] = []
win.analytics["dates"][theday][itemtype][cur].append(
["00:00:00",
"schedule",
path,
"[Un-Checked]",
schedulepath,
username]
)
# RETURNING BACK TO NORMAL
win.url = back
win.current["tool"] = "selection"
analytics.save(win.project, win.analytics)
win.analytics = analytics.load(win.project)
win.checklists = {}
# Multiuser sycning
win.multiuser["request"] = "analytics"
#########################################################
# ICON
UI_elements.image(layer, win,
"settings/themes/"+win.settings["Theme"]+"/icons/schedule.png",
5, win.scroll["schedule"] + 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["schedule"] + current_Y+25,
)
layer.show_text(thing[0][4][-1])
# TASK URL INSIDE THE CHECKLIST
if fullurl:
layer.set_font_size(10)
layer.move_to(
60+len(thing[0][4][-1])*12,
win.scroll["schedule"] + current_Y+25,
)
layer.show_text(fullurl)
# NAME OF THE ASSET / SHOT
if acur in ["chr", "veh", "loc", "obj"]:
assetname = talk.text(acur)+": "+name
else:
assetname = entry[1].replace("/set", "")
layer.set_font_size(15)
layer.move_to(
50,
win.scroll["schedule"] + current_Y+45,
)
layer.show_text(assetname)
# DATE
if entry[0] != "1997/07/30" and entry[0] != today:
layer.set_font_size(15)
layer.move_to(
width-130,
win.scroll["schedule"] + current_Y+45,
)
layer.show_text(entry[0])
# TIME
layer.set_font_size(15)
layer.move_to(
width-130,
win.scroll["schedule"] + current_Y+65,
)
layer.show_text(thing[0][0])
# USERNAME
layer.set_font_size(15)
layer.move_to(
20,
win.scroll["schedule"] + current_Y+65,
)
layer.show_text(talk.text("user_schedules")+" "+thing[0][-1])
# IF SELECTED THERE WILL BE MORE STUFF
if win.current["schedule_task_selected"] == [ thing, entry[1] ]:
UI_color.set(layer, win, "text_normal")
UI_elements.roundrect(layer, win,
1,
win.scroll["schedule"] + current_Y,
width-2,
75,
10,
fill=False)
layer.stroke()
current_Y = current_Y + 85
# If it's a task from an asset or a scene. There should be a link
# to it. But first let's check that's it's infect an asset.
# because...
if entry[1] and entry[1] != "/set":
goto = "script"
itemtype = "scenes"
# If asset:
if acur in ["chr", "veh", "loc","obj"]:
# ICON
UI_elements.image(layer, win,
"settings/themes/"+win.settings["Theme"]+"/icons/"+acur+".png",
25, win.scroll["schedule"] + current_Y+5, 40, 40)
goto = "assets"
itemtype = "assets"
elif not acur:
itemtype = "files"
else:
goto = "script"
itemtype = "scenes"
if goto == "script":
# ICON
UI_elements.image(layer, win,
"settings/themes/"+win.settings["Theme"]+"/icons/scene.png",
25, win.scroll["schedule"] + current_Y+5, 40, 40)
# Here comes the link button
def do():
win.url = goto
win.cur = entry[1]
win.current["asset_left_panel"] = "schedule"
UI_elements.roundrect(layer, win,
20,
win.scroll["schedule"] + current_Y+5,
width-20,
40,
10,
button=do,
tip=entry[1]+" : "+fullurl+thing[0][4][-1],
offset=[x,y],
fill=False)
layer.stroke()
#Title
UI_color.set(layer, win, "text_normal")
layer.set_font_size(20)
layer.move_to(
80,
win.scroll["schedule"] + current_Y+30,
)
layer.show_text(assetname)
current_Y = current_Y + 50
# Next thing! Let's show the most editable values. I think the first
# one. For the director will be important. Is to edit the USER.
# or in other words. Who is doing the task.
# In the end It will be a drop down menu. Like all of it. But for this
# I need to implement MULTIUSER first. And it's not a priority right
# now. So a simple text editor could be okay.
# ICON
UI_elements.image(layer, win,
"settings/themes/"+win.settings["Theme"]+"/icons/user.png",
25, win.scroll["schedule"] + current_Y+5, 40, 40)
UI_elements.text(layer, win, "schedule_username_setting",
80,
win.scroll["schedule"] + current_Y+5,
width-80,
40,
set_text=thing[0][-1],
tip=talk.text("username"),
offset=[x,y])
if win.text["schedule_username_setting"]["text"] != thing[0][-1]:
def do():
thing[0][-1] = win.text["schedule_username_setting"]["text"]
analytics.save(win.project, win.analytics)
# Multiuser sycning
win.multiuser["request"] = "analytics"
UI_elements.roundrect(layer, win,
width-40,
win.scroll["schedule"] + current_Y+5,
40,
40,
10,
button=do,
icon="ok",
tip=talk.text("checked"),
offset=[x,y])
current_Y = current_Y + 50
# Multiuser now gave a way to give who ever does the assingments
# a tool to select the name from a drop down menu. Rather then
# type it by hand. I will still have the ability to type. What
# if that someone is not logged in currently.
if win.textactive == "schedule_username_setting":
# So when you clicked to edit a drop down menu will appear.
for user in win.multiuser["users"]:
# It's going to be simple buttons with user icons and
# the username.
def do():
thing[0][-1] = win.multiuser["users"][user]["username"]
analytics.save(win.project, win.analytics)
win.text["schedule_username_setting"]["text"] = thing[0][-1]
win.textactive = ""
# Multiuser sycning
win.multiuser["request"] = "analytics"
UI_elements.roundrect(layer, win,
80,
win.scroll["schedule"] + current_Y+5,
width-80,
40,
10,
button=do,
icon="user",
tip=win.multiuser["users"][user]["username"],
offset=[x,y])
UI_color.set(layer, win, "text_normal")
layer.set_font_size(20)
layer.move_to(
130,
win.scroll["schedule"] + current_Y+30,
)
layer.show_text(win.multiuser["users"][user]["username"])
current_Y = current_Y + 50
# DATE : TIME
UI_elements.image(layer, win,
"settings/themes/"+win.settings["Theme"]+"/icons/schedule.png",
25, win.scroll["schedule"] + current_Y+5, 40, 40)
UI_elements.text(layer, win, "schedule_date_setting",
80,
win.scroll["schedule"] + current_Y+5,
(width-80)/2-20,
40,
set_text=entry[0],
tip=talk.text("username"),
offset=[x,y])
### DELETE KEY ###
if acur in ["chr", "veh", "loc","obj"]:
itemtype = "assets"
elif not acur:
itemtype = "files"
else:
itemtype = "scenes"
if 65535 in win.current["keys"]:
pop = win.analytics["dates"][entry[0]][itemtype]\
[entry[1]].pop(thing[1])
win.current["keys"] = []
analytics.save(win.project, win.analytics)
win.analytics = analytics.load(win.project)
if win.text["schedule_date_setting"]["text"] != entry[0]\
and analytics.ifdate(win.text["schedule_date_setting"]["text"]):
def do():
pop = win.analytics["dates"][entry[0]][itemtype]\
[entry[1]].pop(thing[1])
if win.text["schedule_date_setting"]["text"] not in win.analytics["dates"]:
win.analytics["dates"][win.text["schedule_date_setting"]["text"]] = {}
if itemtype not in win.analytics["dates"][win.text["schedule_date_setting"]["text"]]:
win.analytics["dates"][win.text["schedule_date_setting"]["text"]][itemtype] = {}
if entry[1] not in win.analytics["dates"][win.text["schedule_date_setting"]["text"]][itemtype]:
win.analytics["dates"][win.text["schedule_date_setting"]["text"]][itemtype][entry[1]] = []
win.analytics["dates"][win.text["schedule_date_setting"]["text"]][itemtype]\
[entry[1]].append(pop)
analytics.save(win.project, win.analytics)
win.analytics = analytics.load(win.project)
# Multiuser sycning
win.multiuser["request"] = "analytics"
UI_elements.roundrect(layer, win,
(width-80)/2+20,
win.scroll["schedule"] + current_Y+5,
40,
40,
10,
button=do,
icon="ok",
tip=talk.text("checked"),
offset=[x,y])
# TIME
UI_elements.text(layer, win, "schedule_time_setting",
80+(width-80)/2,
win.scroll["schedule"] + current_Y+5,
(width-80)/2,
40,
set_text=thing[0][0],
tip=talk.text("username"),
offset=[x,y])
if win.text["schedule_time_setting"]["text"] != thing[0][0]\
and analytics.iftime(win.text["schedule_time_setting"]["text"]):
def do():
thing[0][0] = win.text["schedule_time_setting"]["text"]
analytics.save(win.project, win.analytics)
win.analytics = analytics.load(win.project)
# Multiuser sycning
win.multiuser["request"] = "analytics"
UI_elements.roundrect(layer, win,
width-40,
win.scroll["schedule"] + current_Y+5,
40,
40,
10,
button=do,
icon="ok",
tip=talk.text("checked"),
offset=[x,y])
current_Y = current_Y + 70
else:
current_Y = current_Y + 85
# Outputting the layer
outlayer.set_source_surface(surface, x, y)
outlayer.paint()
# Scroll
UI_elements.scroll_area(outlayer, win, "schedule",
x+0,
y+50,
width,
height-50,
current_Y,
bar=True,
mmb=True)