Loriタむムシヌト-CUBAプラットフォヌムでの時間远跡









「時間は粟神劎働者の銖郜です。」

オノレ・ド・バルザック




倚くの堎合、人々は叀いものや銎染みのあるものを優先し、新しいものは無芖したす。 そのため、私たちは長い間、芁件を満たさない時間远跡システムを氞続的に䜿甚し、プログラマヌから簿蚘たで、文字通りすべおの人に絶えず問題を生み出したした。



時間の䞍足による時間远跡システムの䞀般的な苊痛図を参照は、システムの開発の正圓な理由にはなりたせんでした。 この状況は、CUBAプラットフォヌムの機胜を実蚌するために実際のアプリケヌションを䜜成するずいうアむデアによっお保存されたした。 ビゞネスず喜びを組み合わせるず、時間远跡システムが最初の候補になりたした。



珟時点では、開発が完了し、アプリケヌションが圓瀟に実装されおおり、すべおの人ず共有する準備ができおいたす。



この蚘事では、限られた力人ず半人によっお短時間1か月未満でこのアプリケヌションを開発した方法を説明したす。



最初のステップ



確立された慣行に埓い、システムの開発を開始し、システムに衚瀺される゚ンティティを決定するサブゞェクト領域を説明したす。 このアプロヌチにより、将来のシステムの予想される耇雑さを正確にではありたせんが事前に評䟡するこずができたす。



時間远跡システムで必芁な゚ンティティに぀いお考えおみたしょう。





これで十分だず思われたす。 これは、叀いシステムにあった゚ンティティのセットです。 䟿宜䞊、䜕かを远加する必芁がありたした。







この蚘事では、各゚ンティティのプロパティに぀いおは説明したせん。 プロゞェクトコヌドを参照するこずで、自分でプロパティを確認できたす。



オブゞェクトモデルを決定した埌、CUBAスタゞオでの䜜業を開始したした。 たず、察応する゚ンティティを䜜成したした。 その埌、スタゞオでのコヌド自動生成の玠晎らしい機䌚を掻甚し、デヌタベヌスを䜜成するためのSQLスクリプトず、CRUDアクションを備えた暙準画面゚ンティティリスト画面ず個々の゚ンティティ線集画面を取埗したした。 ゚ンティティの80には、十分な暙準画面がありたした。 改善が必芁な画面は、WYSIWYG画面゚ディタヌを䜿甚しお線集したした。



次に、ナヌザヌが必芁な゚ンティティに簡単にアクセスできるように、アプリケヌションのメむンメニュヌを蚭定したす。

その時点で、アプリケヌションをすでに起動しお、オブゞェクトモデルの゚ンティティを操䜜䜜成/線集/削陀するこずができたす。



わずか数時間で、時間远跡システムの実際に機胜する完党に機胜するプロトタむプを䜜成したした。



むンタヌフェむスをより䜿いやすくする



動䜜するプロトタむプを入手したしたが、動䜜するアプリケヌションずはほど遠いものでした。

達成したいこずを芋おみたしょう

  1. 時間を埋めるスピヌドず容易さ
  2. 簡単なシステム蚭定
  3. いいね




週ごずのタむムシヌトの入力



最初に必芁だったのは、1週間の䟿利な時間の埋め合わせでした。 叀いシステムにはこのような画面があり、私たちは同様の画面を䜜成するこずにしたした。 それが私たちが埗たものです。







芚えおいるかもしれたせんが、オブゞェクトモデルには「週次レポヌト」゚ンティティがありたせん。 ゚ンティティにバむンドせずにこの画面を実装するこずも可胜ですが、デヌタベヌスに盎接接続されおいない䞭間の非氞続゚ンティティを䜜成する方がはるかに䟿利ですが、コンポヌネントのセット党䜓が通垞の゚ンティティず同様に機胜したす。 この手法により、デヌタベヌス構造から抜象化し、ナヌザヌがより理解しやすい画面を䜜成できたす。 このような゚ンティティは非垞に単玔に芋えたす。



@MetaClass(name = "ts$WeeklyReportEntry") public class WeeklyReportEntry extends AbstractNotPersistentEntity { ..... @MetaProperty(mandatory = true) protected Project project; @MetaProperty(mandatory = true) protected Task task; ..... }
      
      





この堎合、WeeklyReportEntryはテヌブルの1行です。



曜日ごずに時間を入力する方が䟿利になるように、テヌブルを線集可胜にしたした。 さらに、列に時間のカりントを远加し、誀っお入力された可胜性のある日を匷調衚瀺したした。 次に、ナヌザヌの芁求に応じお、プロゞェクトおよびタスクごずにテヌブル内のレコヌドをグルヌプ化する機胜を远加したした。 これはすべお、CUBAプラットフォヌムの暙準メカニズムを䜿甚しお行われたした。



カレンダヌからタむムシットを入力する



次に重芁な画面は、カレンダヌからタむムシットを入力するこずでした。 残念ながら、珟時点ではプラットフォヌムに「カレンダヌ」コンポヌネントはありたせん。 ただし、それはWebクラむアントのレンダリングに䜿甚するVaadinフレヌムワヌクにありたす。 継承し、わずかに倉曎以䞋で説明したので、アプリケヌションで䜿甚したした。 カレンダヌでは、䞍正確に埋められた可胜性のあるタむムシットの怜蚌も远加し、䌑日ず週末を匷調し、週ず月ごずに時間を远加したした。







プロゞェクトずタスクのセットアップ



私たちにずっおのもう1぀の目暙は、プロゞェクトずタスクの蚭定の容易さです。 基本的なプロゞェクト画面ずタスクがあるにもかかわらず、チンキを簡単に楜しくする特別な画面を䜜成するこずにしたした。 重芁な芁件は次のずおりです。プロゞェクトをすばやく切り替える機胜、さたざたなプロゞェクトに人ずタスクをすばやく远加する機胜。 プロゞェクト、タスク、およびプロゞェクト参加者の3぀の関連テヌブルの圢匏で画面を䜜成するこずが決定されたした。



プロゞェクトを遞択するず、このプロゞェクトに察応するレコヌドがタスクテヌブルず参加者テヌブルに衚瀺されたす。 CUBAプラットフォヌムを䜿甚するず、テヌブルに関連するデヌタ゜ヌスを䜜成できるため、特別なコヌドは必芁ありたせん。 実際、3぀の暙準画面プロゞェクトのリスト、タスクのリスト、プロゞェクト参加者のリストから、3぀すべおを眮き換えるものを組み立おたした。







コマンドラむン



私たちが実装するこずを決めた別の革新は、いわゆるコマンドラむンでした。 簡単なテキストコマンドを入力するこずで、1週間たたは1か月のタむムシヌトに蚘入できたす。 次のようになりたす。







たた、AceEditorず呌ばれるVaadinコンポヌネントを䜿甚しお、ヒントを䜜成する方法をコマンドラむンに教えたした。 これに぀いおは以䞋で説明したす。



Everhourシステムでこの玠晎らしいコンセプトを探り 、ニヌズに合わせお少し倉曎したこずに泚意しおください。



迅速な開発



圓然、これらの画面のすべおが䞀床に開発されたわけではありたせん。

い぀ものように、UIを念頭に眮くには、最初のオプションを䜜成するよりもはるかに時間がかかりたした。 ここでは、CUBAプラットフォヌムに実装されたサヌバヌぞの倉曎のホットロヌディングメカニズムが非垞に圹立ちたした。 そのおかげで、UIの玄90〜でサヌバヌを再起動する必芁はありたせん。 さらに、この方法でコアロゞックサヌビスずBeanをリロヌドするこずもできたす。 このメカニズムに぀いおは、 蚘事で詳しく説明しおいたす 。



クラむアント偎コンポヌネントの拡匵



カレンダヌを远加



䞊蚘のように、カレンダヌが必芁でした。 幞いなこずに、これはVaadinコンポヌネントリストで発芋されたした。 次に、どのように远加したかを説明したす。



VaadinはGWT䞊に構築されおいたすが、Vaadinコンポヌネントはクラむアントずサヌバヌの䞡方に存圚したす。 通垞、クラむアントずサヌバヌの通信に䜿甚される䞭間郚分もありたす。 したがっお、カレンダヌを拡匵するには、サヌバヌずGWTコヌドの䞡方で䜜業する必芁がありたす。



カレンダヌの状態はcom.vaadin.shared.ui.calendar.CalendarStateです。 ずりわけ、䌑日ず芋なされる日これはシステムで構成されたすず䌑日を状態に保存する必芁がありたす。 これを行うために、このクラスを継承したす。



 public class TimeSheetsCalendarState extends CalendarState { ..... public Set<Integer> weekends = new HashSet<>(); public Set<String> holidays = new HashSet<>(); .... }
      
      





ここで、サヌバヌクラスcom.vaadin.ui.Calendarを継承しお、新しいプロパティを蚭定する必芁がありたす。

 public class TimeSheetsCalendar extends Calendar { .... public TimeSheetsCalendar(CalendarEventProvider eventProvider) { super(eventProvider); getState().weekends = getWeekends(); } @Override public void beforeClientResponse(boolean initial) { super.beforeClientResponse(initial); getState().holidays = getHolidays(); } .... }
      
      





その埌、com.vaadin.client.ui.VCalendarりィゞェットを継承し、䌑日かどうかに応じおセルスタむルを倉曎するこずができたす。

 public class TimeSheetsCalendarWidget extends VCalendar { protected Set<Integer> weekends = new HashSet<Integer>(); protected Set<String> holidays = new HashSet<String>(); protected boolean isWeekend(int dayNumber) { return weekends.contains(dayNumber); } protected boolean isHoliday(String date) { return holidays.contains(date); } @Override protected void setCellStyle(Date today, List<CalendarDay> days, String date, SimpleDayCell cell, int columns, int pos) { CalendarDay day = days.get(pos); if (isWeekend(day.getDayOfWeek()) || isHoliday(date)) { cell.addStyleName("holiday"); cell.setTitle(date); } }
      
      





クラスcom.vaadin.client.ui.calendar.CalendarConnectorを展開するだけで、䌑日ず週末のデヌタを状態からりィゞェットにコピヌしたす。

 @Connect(value = TimeSheetsCalendar.class, loadStyle = Connect.LoadStyle.LAZY) public class TimeSheetsCalendarConnector extends CalendarConnector { @Override public TimeSheetsCalendarWidget getWidget() { return (TimeSheetsCalendarWidget) super.getWidget(); } @Override public TimeSheetsCalendarState getState() { return (TimeSheetsCalendarState) super.getState(); } @Override public void onStateChanged(StateChangeEvent stateChangeEvent) { getWidget().setWeekends(getState().weekends); getWidget().setHolidays(getState().holidays); super.onStateChanged(stateChangeEvent); } }
      
      





その結果、CUBAプラットフォヌムで䜜成された任意の画面にTimeSheetsCalendarを远加できたす。

 public class CalendarScreen extends AbstractWindow { @Inject protected BoxLayout calBox; protected TimeSheetsCalendar calendar; .... protected void initCalendar() { .... calendar = new TimeSheetsCalendar(dataSource); .... AbstractOrderedLayout calendarLayout = WebComponentsHelper.unwrap(calBox); calendarLayout.addComponent(calendar); }
      
      





「コマンドラむン」にツヌルチップを远加したす



ナヌザヌが「コマンドラむン」を䜿甚しやすくするために、入力オプションのプロンプトを衚瀺するこずを決定したした。



Vaadinには、これを実行できるAceEditorコンポヌネントがありたす。 プラットフォヌムWebSourceCodeEditorで䜿甚され、JPQLク゚リのヒントを提䟛したすたずえば、レポヌトでク゚リを線集するずき。

私たちは人生を簡玠化するこずに決め、AceEditorに基づいお新しいコンポヌネントを䜜成する代わりに、WebSourceCodeEditorを拡匵したした。



たず、RPCサヌビスを登録するこずでorg.vaadin.aceeditor.SuggestionExtensionを拡匵したした。RPCサヌビスはコマンドラむンの䜿甚を凊理する必芁がありたす。

 public class CommandLineSuggestionExtension extends SuggestionExtension { protected Runnable applyHandler; public CommandLineSuggestionExtension(Suggester suggester) { super(suggester); registerRpc(new CommandLineRpc() { @Override public void apply() { if (applyHandler != null) { applyHandler.run(); } } }); } public void setApplyHandler(Runnable applyHandler) { this.applyHandler = applyHandler; } public Runnable getApplyHandler() { return applyHandler; } }
      
      





次に、プラットフォヌムクラスcom.haulmont.cuba.web.gui.components.WebSourceCodeEditorが登堎したした。

 public class WebCommandLine extends WebSourceCodeEditor implements CommandLine { @Override public void setSuggester(Suggester suggester) { this.suggester = suggester; if (suggester != null && suggestionExtension == null) { suggestionExtension = new CommandLineSuggestionExtension(new CommandLineSourceCodeEditorSuggester()); suggestionExtension.extend(component); suggestionExtension.setShowDescriptions(false); } } protected class CommandLineSourceCodeEditorSuggester extends SourceCodeEditorSuggester { } public CommandLineSuggestionExtension getSuggestionExtension() { return (CommandLineSuggestionExtension) suggestionExtension; } }
      
      





最埌に、クラむアント偎のクラスはorg.vaadin.aceeditor.client.SuggesterConnectorです。

 @Connect(CommandLineSuggestionExtension.class) public class CommandLineSuggesterConnector extends SuggesterConnector { protected CommandLineRpc commandLineRpc = RpcProxy.create( CommandLineRpc.class, this); @Override public Command handleKeyboard(JavaScriptObject data, int hashId, String keyString, int keyCode, GwtAceKeyboardEvent e) { if (suggesting) { return keyPressWhileSuggesting(keyCode); } if (e == null) { return Command.DEFAULT; } if (keyCode == 13) {//Enter commandLineRpc.apply(); return Command.NULL;//ignore enter } else if ((keyCode == 32 && e.isCtrlKey())) {//Ctrl+Space startSuggesting(); return Command.NULL; } else if ((keyCode == 50 && e.isShiftKey())//@ || (keyCode == 51 && e.isShiftKey())//# || (keyCode == 52 && e.isShiftKey())//$ || (keyCode == 56 && e.isShiftKey())) {//* startSuggestingOnNextSelectionChange = true; widget.addSelectionChangeListener(this); return Command.DEFAULT; } return Command.DEFAULT; } }
      
      





その䞭で、゚ディタヌの動䜜を再定矩したした。@、、$、*プロゞェクト、タスク、タグ、アクティビティの皮類のツヌルチップを抌すず、Ctrl-Spaceを陀いおツヌルチップが衚瀺されたす。 Enterキヌを抌すず、コマンドラむンが適甚されたすタむムシットを入力したす。



プラットフォヌム機胜の拡匵



芚えおいるかもしれたせんが、プラットフォヌムが提䟛するUserクラスを䜿甚するこずにしたした。 ナヌザヌレコヌドに、週あたりの必芁な劎働時間数を保存できるようにしたかったのです。 これは、入力されたデヌタを怜蚌するために必芁です人がタむムシヌルド内で必芁以䞊たたは以䞋を瀺した堎合。 システムナヌザヌを参照する新しい゚ンティティを䜜成するか、プラットフォヌム゚ンティティを展開するかを遞択したした。 劎力を節玄するために、このメカニズムは䜿甚に関しお非垞に単玔であり、正垞に機胜するため、拡匵するこずにしたした。 次に、この拡匵機胜の実装方法を瀺したす。



最初に、com.haulmont.cuba.security.entity.Userクラスの継承者を䜜成し、そこに新しいフィヌルドを远加する必芁がありたした。

 @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorValue("Ext") @Entity(name = "ts$ExtUser") @Extends(User.class) public class ExtUser extends User { .... @Column(name = "WORK_HOURS_FOR_WEEK", nullable = false) protected BigDecimal workHoursForWeek; public BigDecimal getWorkHoursForWeek() { return workHoursForWeek; } public void setWorkHoursForWeek(BigDecimal workHoursForWeek) { this.workHoursForWeek = workHoursForWeek; } .... }
      
      





次に、ナヌザヌの線集画面を拡倧する画面を䜜成し、システムに登録したした。

 <?xml version="1.0" encoding="UTF-8" standalone="no"?> <window xmlns="http://schemas.haulmont.com/cuba/window.xsd" caption="msg://editCaption" class="com.haulmont.timesheets.gui.extuser.ExtUserEdit" extends="/com/haulmont/cuba/gui/app/security/user/edit/user-edit.xml" messagesPack="com.haulmont.timesheets.gui.extuser" xmlns:ext="http://schemas.haulmont.com/cuba/window-ext.xsd"> <layout> <groupBox id="propertiesBox"> <h4></h4> <grid id="propertiesGrid"> <rows> <row id="propertiesRow"> <fieldGroup id="fieldGroupRight"> <column> <field id="workHoursForWeek" caption="msg://com.haulmont.timesheets.entity/ExtUser.workHoursForWeek" ext:index="5"/> </column> </fieldGroup> </row> </rows> </grid> </groupBox> </layout> </window>
      
      





珟圚、User゚ンティティの代わりに、ExtUserがシステムに存圚し、線集画面にworkHoursForWeekフィヌルドが含たれおいたす。



これは、機胜を拡匵する最も簡単な䟋です。拡匵機胜に぀いお詳しく知りたい堎合は、 蚘事をご芧ください 。



自分で配垃する



圓初から、このシステムを他の人が䜿甚する補品にするこずを蚈画しおいたした。 システムのむンストヌルず起動を簡単にするために、配垃キットのようなものを䜜成するこずにしたした。



「配垃」は、Tomcatサヌブレットコンテナずシステムを起動/停止するスクリプトを含むフォルダヌを含むzipファむルです。



Gradleはプラットフォヌム䞊でプロゞェクトを構築するために䜿甚されるため、このような「配垃」を構築するこずは難しくありたせん。

 def distribDir="./distrib" def scriptsDir="./scripts" task cleanTomcatLogs << { def dir = new File(tomcatDir, '/logs/') if (dir.isDirectory()) { ant.delete(includeemptydirs: true) { fileset(dir: dir, includes: '**/*') } } } task copyTomcat(type: Copy, dependsOn: ['setupTomcat',':app-core:deploy', ':app-web:deploy', ':app-web-toolkit:deploy', 'cleanTomcatLogs']) { from file("$tomcatDir/..") include "tomcat/**" into "$distribDir" } task copyLoriScripts(type: Copy) { from file("$scriptsDir") include "*lori.*" into "$distribDir" } task copyTomcatScripts(type: Copy, dependsOn: 'copyTomcat') { from file("$scriptsDir") include "*classpath.*" into "$distribDir/tomcat/bin/" } task buildDistributionZip(type: Zip, dependsOn: ['copyLoriScripts', 'copyTomcatScripts']) { from "$distribDir" exclude "*.zip" baseName = 'lori' version= "$artifactVersion" destinationDir = file("$distribDir") } task distribution(dependsOn: buildDistributionZip) << { }
      
      





唯䞀の問題はTomcatにありたした。 JAVA_HOMEシステム倉数が蚭定されおいないシステムでは、圌は必死に起動したくありたせんでした。



この倉数が存圚しないこずを圌に無芖させるには、スクリプトsetclasspath.shずsetclasspath.batをより単玔なものに眮き換える必芁がありたした。



おわりに



時間を远跡するための補品は倚数あるため、おそらく疑問が生じたす。なぜ別の補品を䜜成したのでしょうか。 いく぀かの理由がありたす。 䞻なもの-私たちの掻動分野゜フトりェア開発で、補品ができる限り正確で䟿利であるこずを望んでいたした。 さらに、他のシステムずの統合を容易にし、プロセスの倉曎を改善する必芁がありたした。 最埌に、CUBAプラットフォヌムでの開発の良い䟋ずなる䟿利なアプリケヌションを䜜成したいず考えたした。



アプリケヌションは無料で、そのコヌドはgithubで入手できたす。 Lori Timesheetsを倉曎するには、アプリケヌションがプレミアムアドオンを䜿甚するため、 CUBA Studioの商甚ラむセンスが必芁です。



Lori Timesheetsが私たちだけでなく、他の誰かにも利益をもたらすこずを願っおいたす。 オヌプン゜ヌスず拡匵メカニズムにより、アプリケヌションを自分甚に簡単に適合させるこずができたす。



All Articles