chore: re-initialize git repository for deployment

This commit is contained in:
yi
2026-06-10 14:01:56 +08:00
commit 7783e965fd
131 changed files with 14355 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
default_app_config = 'workflow.apps.WorkflowConfig'
+118
View File
@@ -0,0 +1,118 @@
from django.contrib import admin
from django.contrib.contenttypes.models import ContentType
from common import generic
from workflow.models import Modal,Node,TodoList,Instance,History
class NodeInline(admin.TabularInline):
model = Node
fields = ['code','name','next_user_handler','can_deny','can_terminate']
def get_extra(self, request, obj=None, **kwargs):
if obj:
return 0
else:
return 1
class WorkflowModelAdmin(admin.ModelAdmin):
list_display = ['code','name','begin','end']
inlines = [NodeInline]
readonly_fields = ['app_name','model_name']
raw_id_fields = ['content_type']
fields = (
('begin','end',),('code','name',),('description',),('content_type',),('app_name','model_name',),
)
def get_form(self, request, obj=None, **kwargs):
print kwargs
return super(WorkflowModelAdmin,self).get_form(request,obj,**kwargs)
def save_model(self, request, obj, form, change):
import datetime
super(WorkflowModelAdmin,self).save_model(request,obj,form,change)
if not obj.code:
code = 'WF%03d' % obj.id
obj.code = code
obj.save()
if not obj.begin:
obj.begin = datetime.date.today()
obj.end = datetime.date(9999,12,31)
app_name = obj.content_type.app_label
model_name = obj.content_type.model
obj.app_name = app_name
obj.model_name = model_name
obj.save()
class WorkflowNodeAdmin(admin.ModelAdmin):
fields = (
('modal',),
('name','code',),
('start','stop','can_terminate','can_deny','can_edit',),
('email_notice','short_message_notice','approve_node',),('next',),
('handler_type',),
('handler',),
('next_user_handler','next_node_handler',),
('status_field','status_value',),('action',),
('users',),('positions',),('roles',),
)
list_display = ['code','name','modal','can_deny','can_terminate']
list_display_links = ['code','name']
list_filter = ['modal']
readonly_fields = ['modal','code']
filter_horizontal = ['next','users','positions','roles','departments',]
radio_fields = {'handler_type':admin.HORIZONTAL}
search_fields = ['modal__code','modal__name','code','name']
def get_readonly_fields(self, request, obj=None):
if obj and obj.stop:
return ['modal','code','next']
return super(WorkflowNodeAdmin,self).get_readonly_fields(request,obj)
def formfield_for_manytomany(self, db_field, request=None, **kwargs):
if db_field.name == 'next':
apps = generic.get_app_model_info_from_request(request)
# print apps
if apps and apps.get('obj'):
# print 'it is here'
obj = apps.get('obj')
# print obj.modal
kwargs['queryset'] = Node.objects.filter(modal=obj.modal).exclude(id=obj.id)
else:
kwargs['queryset'] = Node.objects.filter(id=-1)
return super(WorkflowNodeAdmin,self).formfield_for_manytomany(db_field,request,**kwargs)
class InstanceAdmin(admin.ModelAdmin):
list_display = ['code','modal','starter','start_time','status']
readonly_fields = ['code','modal','starter','start_time','status','object_id','approved_time','current_nodes']
class TodoAdmin(admin.ModelAdmin):
list_display = ['code_link','modal_dsc','href','node_dsc','is_read','status','submitter','arrived_time']
list_filter = ['status']
def get_queryset(self, request):
return super(TodoAdmin,self).get_queryset(request).filter(user=request.user)
class HistoryAdmin(admin.ModelAdmin):
list_display = ['inst','node','user','pro_time','memo']
class ContentTypeAdmin(admin.ModelAdmin):
list_display = ['app_label','model']
search_fields = ['app_label','model']
list_per_page = 20
list_filter = ['app_label']
admin.site.register(Modal, WorkflowModelAdmin)
admin.site.register(Node, WorkflowNodeAdmin)
admin.site.register(Instance, InstanceAdmin)
admin.site.register(TodoList, TodoAdmin)
admin.site.register(History, HistoryAdmin)
admin.site.register(ContentType,ContentTypeAdmin)
+9
View File
@@ -0,0 +1,9 @@
__author__ = 'zhugl'
# created at 15-4-22
from django.apps.config import AppConfig
from django.utils.translation import ugettext_lazy as _
class WorkflowConfig(AppConfig):
name = 'workflow'
verbose_name = _('workflow')
+272
View File
@@ -0,0 +1,272 @@
# coding=utf-8
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import User
from django.utils.html import format_html
from django.utils.translation import ugettext_lazy as _
from common import const
from syscfg.models import Role
from organ.models import Position,OrgUnit
class Modal(models.Model):
"""
"""
import datetime
index_weight = 1
code = models.CharField(_("workflow code"),max_length=const.DB_CHAR_CODE_6,blank=True,null=True)
name = models.CharField(_("workflow name"),max_length=const.DB_CHAR_NAME_40)
description = models.TextField(_("description"),blank=True,null=True)
content_type = models.ForeignKey(ContentType,verbose_name=_("content type"),limit_choices_to={"app_label__in":['basedata','organ']})
app_name = models.CharField(_("app name"),max_length=const.DB_CHAR_NAME_60,blank=True,null=True)
model_name = models.CharField(_("model name"),max_length=const.DB_CHAR_NAME_60,blank=True,null=True)
# added by zhugl 2015-05-10
begin = models.DateField(_("begin date"),blank=True,null=True,default=datetime.date.today)
end = models.DateField(_("end date"),blank=True,null=True,default=datetime.date(9999,12,31))
def __unicode__(self):
return "%s" % self.name
class Meta:
verbose_name = _("workflow model")
verbose_name_plural = _("workflow model")
class Node(models.Model):
"""
submitter()
upper()
user()
role()
position()
sql()
etc:upper('zhangsan','lisi')
"""
HANDLER_TYPE = (
(1, _("designated user")),
(2, _("designated position")),
(3, _("designated role")),
(4, _("submitter")),
)
index_weight = 2
modal = models.ForeignKey(Modal,verbose_name=_("workflow model"))
code = models.CharField(_("node code"),max_length=const.DB_CHAR_CODE_4,blank=True,null=True)
name = models.CharField(_("node name"),max_length=const.DB_CHAR_NAME_80)
tooltip = models.CharField(_('tooltip words'),blank=True,null=True,max_length=const.DB_CHAR_NAME_120)
start = models.BooleanField(_("start node"),default=False)
stop = models.BooleanField(_("stop node"),default=False)
can_terminate = models.BooleanField(_("can terminate"),default=False)
can_deny = models.BooleanField(_("can deny"),default=True)
can_edit = models.BooleanField(_("can edit"),default=False)
email_notice = models.BooleanField(_("email notice"),default=True)
short_message_notice = models.BooleanField(_("short message notice"),default=False)
approve_node = models.BooleanField(_("approve node"),default=False)
handler = models.TextField(_("handler"),blank=True,null=True,help_text=u'自定义SQL语句,优先高于指定用户、岗位、角色')
# added by zhugl 2015-05-10
handler_type = models.IntegerField(_("handler type"),choices=HANDLER_TYPE,default=1)
positions = models.ManyToManyField(Position,verbose_name=_("designated position"),blank=True)
roles = models.ManyToManyField(Role,verbose_name=_("designated role"),blank=True)
users = models.ManyToManyField(User,verbose_name=_("designated user"),blank=True)
departments = models.ManyToManyField(OrgUnit,verbose_name=_("designated department"),blank=True)
next = models.ManyToManyField('self',blank=True,verbose_name=_("next node"),symmetrical=False)
# added by zhugl 2015-06-30
next_user_handler = models.CharField(_('next user handler'),blank=True,null=True,max_length=const.DB_CHAR_NAME_40)
next_node_handler = models.CharField(_('next node handler'),blank=True,null=True,max_length=const.DB_CHAR_NAME_40)
status_field = models.CharField(_('status field'),blank=True,null=True,max_length=const.DB_CHAR_NAME_40)
status_value = models.CharField(_('status value'),blank=True,null=True,max_length=const.DB_CHAR_NAME_40)
action = models.CharField(_('execute action'),blank=True,null=True,max_length=const.DB_CHAR_NAME_40)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
if not self.code:
fmt = 'N%02d'
self.code = fmt % (self.modal.node_set.count()+1)
super(Node,self).save(force_insert,force_update,using,update_fields)
def __unicode__(self):
return "%s-%s" % (self.code, self.name)
class Meta:
verbose_name = _("workflow node")
verbose_name_plural = _("workflow node")
class Instance(models.Model):
"""
"""
STATUS = (
(1, _("NEW")),
(2, _("IN PROGRESS")),
(3, _("DENY")),
(4, _("TERMINATED")),
(9, _("APPROVED")),
(99, _("COMPLETED"))
)
index_weight = 3
code = models.CharField(_("code"),blank=True,null=True,max_length=const.DB_CHAR_CODE_10)
modal = models.ForeignKey(Modal,verbose_name=_("workflow model"))
object_id = models.PositiveIntegerField(_("object id"))
starter = models.ForeignKey(User,verbose_name=_("start user"),related_name="starter")
start_time = models.DateTimeField(_("start time"),auto_now_add=True)
approved_time = models.DateTimeField(_("approved time"),blank=True,null=True)
status = models.IntegerField(_("status"),default=1,choices=STATUS)
current_nodes = models.ManyToManyField(Node,verbose_name=_("current node"),blank=True)
def __unicode__(self):
return '%s' % self.code
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
super(Instance,self).save(force_insert,force_update,using,update_fields)
if not self.code:
self.code = 'S%05d'%self.id
self.save()
class Meta:
verbose_name = _("workflow instance")
verbose_name_plural = _("workflow instance")
class History(models.Model):
"""
workflow history
"""
PROCESS_TYPE = (
(0, _("SUBMIT")),
(1, _("AGREE")),
(3, _("DENY")),
(4, _("TERMINATE")),
)
index_weight = 5
inst = models.ForeignKey(Instance,verbose_name=_("workflow instance"))
node = models.ForeignKey(Node,verbose_name=_("workflow node"),blank=True,null=True)
user = models.ForeignKey(User,verbose_name=_("submitter"))
pro_time = models.DateTimeField(_("process time"),auto_now_add=True)
pro_type = models.IntegerField(_("process type"),choices=PROCESS_TYPE,default=0)
memo = models.CharField(_("memo"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True)
def get_node_desc(self):
if self.node:
return self.node.name
else:
return u'启动'
def get_action_desc(self):
action_mapping = {0:u'提交',1:u'同意',3:u'拒绝',4:u'终止',}
# print action_mapping
if self.pro_type:
return action_mapping[self.pro_type]
else:
return u'提交'
def get_memo_desc(self):
if self.memo:
return self.memo
else:
return ''
class Meta:
verbose_name = _("workflow history")
verbose_name_plural = _("workflow history")
ordering = ['inst','pro_time']
class TodoList(models.Model):
"""
"""
index_weight = 4
code = models.CharField(_("code"),max_length=const.DB_CHAR_CODE_10,blank=True,null=True)
inst = models.ForeignKey(Instance,verbose_name=_("workflow instance"))
node = models.ForeignKey(Node,verbose_name=_("current node"),blank=True,null=True)
app_name = models.CharField(_("app name"),max_length=const.DB_CHAR_NAME_60,blank=True,null=True)
model_name = models.CharField(_("model name"),max_length=const.DB_CHAR_NAME_60,blank=True,null=True)
user = models.ForeignKey(User,verbose_name=_("handler"))
arrived_time = models.DateTimeField(_("arrived time"),auto_now_add=True)
is_read = models.BooleanField(_("is read"),default=False)
read_time = models.DateTimeField(_("read time"),blank=True,null=True)
status = models.BooleanField(_("is done"),default=False)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
super(TodoList,self).save(force_update,force_update,using,update_fields)
if not self.code:
self.code = 'TD%05d' % self.id
self.save()
def node_dsc(self):
if self.node:
return u'%s'%self.node.name
else:
return u'启动'
def code_link(self):
return format_html("<a href='/admin/{}/{}/{}'>{}</a>",
self.app_name,self.model_name,self.inst.object_id,self.code)
code_link.allow_tags = True
code_link.short_description = _("code")
def href(self):
import sys
reload(sys)
sys.setdefaultencoding("utf-8")
ct = ContentType.objects.get(app_label=self.app_name,model=self.model_name)
obj = ct.get_object_for_this_type(id=self.inst.object_id)
title = u"%s" % (obj)
return format_html("<a href='/admin/{}/{}/{}'>{}</a>",
self.app_name,self.model_name,self.inst.object_id,title)
def modal_dsc(self):
return u'%s'%(self.inst.modal.name)
modal_dsc.short_description = u'业务流程'
def start_time(self):
return self.inst.start_time.strftime('%Y-%m-%d %H:%M')
href.allow_tags = True
href.short_description = _("description")
node_dsc.short_description = _('current node')
def submitter(self):
if self.inst.starter.last_name or self.inst.starter.first_name:
return u"%s%s"%(self.inst.starter.last_name,self.inst.starter.first_name)
return u"%s"%(self.inst.starter.username)
submitter.short_description = _("submitter")
class Meta:
verbose_name = _("workflow todo")
verbose_name_plural = _("workflow todo")
ordering = ['user','-arrived_time']
def get_modal(app_label,model_name):
"""
:param app_label:
:param model_name:
:return:
"""
try:
return Modal.objects.get(app_name=app_label,model_name=model_name)
except Exception,e:
return None
def get_instance(obj):
"""
:param obj:
:return:
"""
if obj and obj._meta:
modal = get_modal(obj._meta.app_label,obj._meta.model_name)
if modal:
try:
return Instance.objects.get(modal=modal,object_id=obj.id)
except Exception,e:
return None
else:
return None
+3
View File
@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.
+314
View File
@@ -0,0 +1,314 @@
# coding=utf-8
from django.contrib.admin import site
from django.contrib.contenttypes.models import ContentType
from django.db import connection
from django.http.response import HttpResponseRedirect
from django.utils.encoding import force_text
from django.template.response import TemplateResponse
from django.contrib import messages
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
from workflow.models import Modal,Instance,TodoList,History,Node
from plugin.wfnodes import NextNodeManager,NextNodeHandler
from plugin.wfusers import NextUserManager,NextUserHandler
from plugin.wfactions import WorkflowActionManager,WorkflowAction
SELECTED_CHECKBOX_NAME = 'NEXT_NODE_USER'
def compile_node_handler(request,obj,next_node):
"""
:param request:
:param obj:
:param handler:
:return:
"""
handler = next_node.handler
next_user_handler = next_node.next_user_handler
# next_user_handler 具有最高优先级
if next_user_handler:
# print 'it is here'
klass = NextUserManager().handlers.get(next_user_handler)
if klass and isinstance(klass,NextUserHandler):
return klass.handle(request,obj,next_node)
if handler and handler != '':
handler = handler.replace("submitter()", request.user.username)
handler = handler.replace("suber()", request.user.username)
fields = obj._meta.fields
for field in fields:
name = field.name
temp = "{{%s}}" % name
val = getattr(obj,name,None)
if val:
if type(val)!=str:
val = str(val)
handler = handler.replace(temp,val)
cursor = connection.cursor()
cursor.execute(handler)
rows = [row for row in cursor.fetchall()]
return rows
else:
tp = next_node.handler_type
if tp == 1 and next_node.users:
# user
users = [user for user in next_node.users.all()]
return users
elif tp == 2 and next_node.positions:
# position
users = []
for position in next_node.positions.all():
for employee in position.employee_set.all():
users.append(employee.user)
return users
elif tp == 3 and next_node.roles:
# role
users = []
for role in next_node.roles.all():
for user in role.users.all():
users.append(user)
return users
elif tp == 4:
# submitter
return request.user
else:
return None
def start(request,app,model,object_id):
"""
:param request:
:return:
"""
import datetime
content_type = ContentType.objects.get(app_label=app,model=model)
obj = content_type.get_object_for_this_type(id=int(object_id))
title = _("Are you sure?")
opts = obj._meta
objects_name = force_text(opts.verbose_name)
has_workflow = False
queryset = Modal.objects.filter(content_type=content_type,end__gt=datetime.date.today()).order_by('-end')
cnt = queryset.count()
workflow_modal = None
next_node = None
next_users = []
has_next_user = False
if cnt > 0:
has_workflow = True
workflow_modal = queryset[0]
query_start_node = workflow_modal.node_set.filter(start=1)
query_first_node = workflow_modal.node_set.order_by('id')
if query_start_node.count() > 0:
next_node = query_start_node[0]
elif query_first_node.count()>0:
next_node = query_first_node[0]
if next_node:
next_users = compile_node_handler(request,obj,next_node)
if len(next_users) > 0:
has_next_user = True
else:
title = _("No workflow model was found")
try:
tmp = Instance.objects.get(modal = workflow_modal,object_id=object_id)
messages.warning(request,_("the object is already in workflow"))
return HttpResponseRedirect("/admin/%s/%s/%s"%(app,model,object_id))
except Exception:
pass
if request.POST.get("post"):
val = request.POST.getlist(SELECTED_CHECKBOX_NAME)
workflow_inst = Instance.objects.create(modal=workflow_modal,object_id=object_id,starter=request.user)
workflow_inst.current_nodes.add(next_node)
workflow_inst.save()
workflow_history = History.objects.create(inst=workflow_inst,user=request.user)
for user in User.objects.filter(id__in=val):
todo = TodoList.objects.create(inst=workflow_inst,node=next_node,user=user,app_name=app,model_name=model)
TodoList.objects.create(inst=workflow_inst,user=request.user,app_name=app,model_name=model,is_read=True,
read_time=datetime.datetime.now(),status=True)
if next_node.status_field and next_node.status_value:
try:
setattr(obj,next_node.status_field,next_node.status_value)
obj.save()
except Exception,e:
pass
messages.success(request,_('workflow started successfully'))
return HttpResponseRedirect("/admin/%s/%s/%s"%(app,model,object_id))
context = dict(
site.each_context(request),
title=title,
opts=opts,
objects_name=objects_name,
object=obj,
has_workflow = has_workflow,
workflow_modal = workflow_modal,
next_node = next_node,
has_next_user = has_next_user,
next_users = next_users,
checkbox_name = SELECTED_CHECKBOX_NAME,
)
request.current_app = site.name
return TemplateResponse(request,'default/workflow/workflow_start_confirmation.html', context)
def approve(request,app,model,object_id,operation):
"""
:param request:
:param operation:
:return:
"""
if operation not in ('1','3','4'):
messages.warning(request,_("unkown workflow operation"))
return HttpResponseRedirect("/admin/%s/%s/%s"%(app,model,object_id))
import datetime
import copy
content_type = ContentType.objects.get(app_label=app,model=model)
obj = content_type.get_object_for_this_type(id=int(object_id))
title = _("Are you sure?")
opts = obj._meta
objects_name = force_text(opts.verbose_name)
has_workflow = False
queryset = Modal.objects.filter(content_type=content_type,end__gt=datetime.date.today()).order_by('-end')
cnt = queryset.count()
workflow_modal = None
if cnt > 0:
workflow_modal = queryset[0]
else:
messages.warning(request,_("No workflow model was found"))
return HttpResponseRedirect("/admin/%s/%s/%s"%(app,model,object_id))
workflow_instance = None
try:
workflow_instance = Instance.objects.get(modal = workflow_modal,object_id=object_id)
except Exception:
messages.warning(request,_("please start the workflow first"))
return HttpResponseRedirect("/admin/%s/%s/%s"%(app,model,object_id))
next_nodes = []
node_users = []
is_stop_node = False
node_has_users = False
delete_instance = False
deny_to_first = False
next_node_description = None
all_nodes =[x for x in Node.objects.filter(modal=workflow_modal).order_by('-id')]
current = workflow_instance.current_nodes.all()
current_tmp = copy.deepcopy(current[0])
if operation == '4' or operation == '3':
next_nodes = ['stop']
is_stop_node = True
else:
if current.count() > 1:
pass
else:
tmp_node = current[0]
if tmp_node.stop or tmp_node == all_nodes[0]:
next_nodes = ['stop']
is_stop_node = True
else:
if tmp_node.next_node_handler and len(tmp_node.next_node_handler) > 0:
hd = tmp_node.next_node_handler
klass = NextNodeManager().handlers.get(hd)
if klass and isinstance(klass,NextNodeHandler):
next_nodes = klass.handle(request,obj,tmp_node)
next_node_description = klass.description
if next_nodes and len(next_nodes) > 0:
pass
elif tmp_node.next and tmp_node.next.count()>0:
next_nodes = [nd for nd in tmp_node.next.all()]
else:
position = all_nodes.index(tmp_node)
next_nodes = [all_nodes[position-1]]
if request.POST.get("post"):
from django.db import transaction
val = request.POST.getlist(SELECTED_CHECKBOX_NAME)
memo = request.POST['memo']
with transaction.atomic():
try:
if delete_instance:
workflow_instance.delete()
else:
if is_stop_node:
workflow_instance.status = 99
if operation in ('3', '4'):
workflow_instance.status = int(operation)
if not workflow_instance.approved_time and operation == '1':
workflow_instance.approved_time = datetime.datetime.now()
workflow_instance.current_nodes.clear()
workflow_instance.save()
else:
workflow_instance.current_nodes.clear()
workflow_instance.current_nodes.add(next_nodes[0])
for user in User.objects.filter(id__in=val):
todo = TodoList.objects.create(inst=workflow_instance,node=next_nodes[0],user=user,app_name=app,model_name=model)
if current_tmp.status_field and current_tmp.status_value:
try:
setattr(obj,current_tmp.status_field,current_tmp.status_value)
obj.save()
except Exception,e:
pass
History.objects.create(inst=workflow_instance,user=request.user,pro_type=int(operation),memo=memo,node=current_tmp)
TodoList.objects.filter(inst=workflow_instance,node=current_tmp,status=0).update(status=1)
messages.success(request,_('workflow approved successfully'))
except Exception,e:
messages.error(request,e)
pass
if current_tmp.action and len(current_tmp.action) > 0:
action = WorkflowActionManager().actions.get(current_tmp.action)
if action and isinstance(action,WorkflowAction):
action.action(request,obj,current_tmp,operation)
return HttpResponseRedirect("/admin/%s/%s/%s"%(app,model,object_id))
if len(next_nodes) > 0 and not is_stop_node and operation == '1':
for node in next_nodes:
users = compile_node_handler(request,obj,node)
if len(users) > 0:
node_has_users = True
node_users.append({'node':node,'users':users})
context = dict(
site.each_context(request),
title=title,
opts=opts,
objects_name=objects_name,
object=obj,
operation = operation,
is_stop_node = is_stop_node,
delete_instance = delete_instance,
node_users = node_users,
node_has_users = node_has_users,
checkbox_name = SELECTED_CHECKBOX_NAME,
)
if next_node_description:
context.update(dict(next_node_description=next_node_description))
return TemplateResponse(request,"default/workflow/workflow_approve_confirmation.html",context)
def restart(request,app,model,object_id,instance):
"""
:param request:
:param app:
:param model:
:param object_id:
:return:
"""
try:
inst = Instance.objects.get(id=int(instance))
if request.user == inst.starter:
inst.delete()
messages.success(request,_("workflow restarted success"))
else:
messages.warning(request,_("you do not have the permission to restart,only the starter can restart"))
except Exception,e:
messages.error(request,e)
return HttpResponseRedirect("/admin/%s/%s/%s"%(app,model,object_id))