| Id | Title | Status |
|---|---|---|
| 0060 | Group entries by type. | OPEN |
| 0061 | Typedef struct statement does not include struct comments. | OPEN |
| 0062 | Long entry name deforms HTML table. | OPEN |
| 0067 | Ccdoc does not correctly reset the access specifiers for methods in base classes that are derived as protected or private. | OPEN |
| 0093 | Add javadoc style doc comment inheritance. | OPEN |
| 0094 | Comment reuse for overloaded functions. | OPEN |
| 0096 | Provide an @repeat operator to avoid having to re-type comments. | OPEN |
| 0105 | The javadoc @see tag defines the label part as optional. | OPEN |
| 0106 | The javadoc spec for @see allows unqualified names as well as qualified names. | OPEN |
| 0108 | -rptdefasd, -rptdefsd and -rptdefv problems and suggestions | OPEN |
| 0113 | Merge separate ccdoc databases. | OPEN |
| 0119 | Inconsistent command line syntax for -html paths under DOS. | OPEN |
| 0121 | Reuse namespace comments between packages. | OPEN |
| 0126 | CcDoc does not recognize ccdoc c++ short comment in a "typedef struct" definition. | OPEN |
| 0128 | Order class entities the same way that were ordered in v07. | OPEN |
| 0131 | Support comma separated declarations. | OPEN |
| 0132 | support single-line prefix comments via //@+ | OPEN |
| 0137 | The {@link} directive does not work @param clauses. | OPEN |
| 0143 | Support javadocs frames format. | OPEN |
| 0148 | Allow users to customize ccdoc output using a CSS file. | OPEN |
| 0151 | Translate the generated html to other languages. | OPEN |
| 0165 | Doxygen compatibility - support virtual grouping of definitions. | OPEN |
| 0167 | In German we want to use embedded dots in the short description. | OPEN |
| 0175 | Generate an index in HTML and XML. | OPEN |
| 0176 | Enhance the table of contents for easier navigation with letter based links at the top. | OPEN |
| 0177 | Invalid link from parameter variable name to member variable. | OPEN |
| 0178 | Namespace/anonymous enum interaction bug. | OPEN |
| 0179 | Add a new @entity directive to allow referencing. | OPEN |
| 0180 | Limit the length of generated file names. | OPEN |
| 0181 | Copy constructor documented incorrectly under some conditions. | OPEN |
| 0182 | Add directory tree/outline expansion (+/-) capabilities to better organize the information. | OPEN |
|
Id: 0060
[Top] [Summary] [Next] |
In many situations it's very bad, that all entities are droped into one big list, you know, under the "Contents" tite, like this:Solution Description:This would be much better in many cases:CONTENTS Entry Type Scope Shor Description b function ... c macro ... d function ... e package ... f macro ...CONTENTS Packages Entry Type Scope Short Description e package ... Functions Entry Type Scope Short Description b function ... d function ... Macros Entry Type Scope Short Description c macro ... f macro ...A switch like -[no]grpbytype? I don't hink so, cos it's not enough generic (E.g. maybe an alphabetical index is required too). I think the real solution is to allow user to specify any number of tables, with custom grouping and title. Something like this:
It would generate two tables: one with title "Contents" and with grouping by entity type, and below another table with title "Index" without grouping (just an alphabetiacal index, like the table in the current version). I put the grouping condition into parenthesis to allow multi-level grouping for future developmens, like -contents(type,functionality).$ ccdoc ... -table(type) "Contents" -table() "Index" ... (Note that I have skipped unimportant parts with ellipsis)
This is an interesting suggestion. The previous version of ccdoc did use to group by entity. I will revisit that decision.
|
Id: 0061
[Top] [Summary] [Next] [Previous] |
The fix for issue 0054 only recognizes ccdoc style comments for the typedef, not for the structure.Solution Description:
This occurs because ccdoc only associates comments with statements.In this case there is only one statement but it is clear that it could be broken into the struct definition and the typedef. It might be possible to modify ccdoc with a heuristic that recognizes this construct and breaks it into two.
This is a low priority enhancement because this syntax is C artifact that is not necessary in C++ because the struct [name] declares [name] as a type.
|
Id: 0062
[Top] [Summary] [Next] [Previous] |
Just an idea: Long entry name stretches the first column too much. In the worst case it makes the "Contents" table unusable, since it stretches the first column so much that there will be no sufficient space for the short description.Solution Description:A possible solution is to give a switch to specify the maximum entry name length, then break long names into two (or three, etc...) with an "\\
\\" in the HTML (to be more precise, with '\\
' + indent*' ' + '\\' (two backslash in the HTML output, not C escaping of one backslash...))
|
Id: 0067
[Top] [Summary] [Next] [Previous] |
Code Sample:Solution Description:class some_base_class { public: /** Must be reported as "protected" in some_class_01 and * as "private" in some_class_02, but reported as "public" */ void some_public_method(); protected: /** Must be reported as "protected" in some_class_01 and * as "private" in some_class_02, but reported as "protected" */ void some_protected_method(); }; class some_class_01 : protected some_base_class {}; class some_class_02 : private some_base_class {};
Ccdoc always reports methods as they appear in the original class specification and expects the user to understand how the access-specification changes if protected or private inheritance is used.This may be difficult for many users. I will look into changing this behavior or making it optional via a switch. Meanwhile, if you don't want the users to see the methods, you can work around this as follows:
class some_base_class { public: /** Must be reported as "protected" in some_class_01 and * as "private" in some_class_02, but reported as "public" */ void some_public_method(); protected: /** Must be reported as "protected" in some_class_01 and * as "private" in some_class_02, but reported as "protected" */ void some_protected_method(); }; class some_class_01 #if !defined(__ccdoc__) : protected some_base_class #endif {}; class some_class_02 #if !defined(__ccdoc__) : private some_base_class #endif {};
|
Id: 0093
[Top] [Summary] [Next] [Previous] |
Javadoc has quite an advanced feature for doc comment inheritance, see http://java.sun.com/j2se/1.4/docs/tooldocs/win32/javadoc.html#inheritingcomments.Solution Description:I would very much appreciate doc comment inheritance, prefferably according to javadoc 1.4. Javadoc 1.3 (http://java.sun.com/j2se/1.3/docs/tooldocs/win32/javadoc.html#inheritingcomments) is not so advanced but would be quite useful also.
This is an excellent idea. I will look into how feasible it is for ccdoc. The only issue is how well ccdoc can recognize these relationships.
|
Id: 0094
[Top] [Summary] [Next] [Previous] |
Previously I suggested that you implement doc comment inheritance. If you decide to do so I have another suggestion.Solution Description:I believe that the javadoc rules says that a method must override a method in the ancestor class to reuse a doc comment. Doc comments cannot be reused for constructors, destructors etc.
I find it quite tedious to write doc comments for constructors and destructors. It would save a lot of typing if one could reuse doc comments beween them.
I can see that the implementation can be difficult, but there is another enhancement that could simplify a lot. Lets call it doc comment reuse of overloaded functions. There is no such thing in javadoc, but maybe there should be. I have read the javadoc spec over and over since I really believed there was such a rule, but there is no rule.
Doc comment reuse of overloaded functions
If an overloaded member function does not have a doc comment, and placed after a function it overloads, it will inherit the doc comment of that function. The names of the parameters of the overloaded functions must be same, only types may vary.The result is that one only needs to document the first function in a group of overloaded functions.
Doc comment reuse of constuctors etc
Hope that you will excuse me doing specification by example: If a member function (constructor, destructor, etc) uses the class name itself in the parameter list, a second "invisible" function is generated, replacing the current class name with a superficial "this". Using the superficial type "this" is good because it is a reserved word in C++ preventing any real class to be defined.If the original function has a doc comment the generated function will be placed after the original one, inheriting its doc comment using the rule for overloaded functions.
If the original function doesn't have a doc comment, the generated one will be placed before the original one, allowing the original one to inherit from the generated one using the rule for overloaded functions.
A code example:
Theses classes will internally be represented as something like this:class Base { /** Default construcor */ Base(); /** * Copy constructor. * * @param src is the object to copy from */ Base(const Base &src); /** Destructor */ ~Base(); /** * Assignment operator. * * @param src is the object to copy from * @return a const reference to this object */ const Base& operator=(const Base& src); } // class Base class Derived : public Base { Derived(); // inherit doc comment Derived(const Derived &src); // inherit doc comment ~Derived(); // inherit doc comment const Derived& operator=(const Derived & src); // inherit doc comment /** * Strange copy constructor that slices the object. * * @param src is the base class object to copy from */ Derived(const Base &src); } // class Derivedclass Base { /** Default construcor */ Base(); this(); // inherit doc comment from previous using overload rule /** * Copy constructor. * * @param src is the object to copy from */ Base(const Base &src); this(const this &src);// inherit doc comment from previous using overload rule /** Destructor */ ~Base(); ~this();// inherit doc comment from previous using overload rule /** * Assignment operator. * * @param src is the object to copy from * @return a const reference to this object */ const Base& operator=(const Base& src); const this& operator=(const this& src);// inherit doc comment from previous using overload rule } // class Base class Derived : public Base { this(const this &src); // this one will pick up doc comment from base class Derived(); // inherit doc comment from previous using overload rule this(const this &src); // this one will pick up doc comment from base class Derived(const Derived &src); // inherit doc comment from previous using overload rule ~this(); // this one will pick up doc comment from base class ~Derived(); // inherit doc comment from previous using overload rule const this& operator=(const this& src); // this one will pick up doc comment from base class const Derived& operator=(const Derived & src); // inherit doc comment from previous using overload rule /** * Strange copy constructor that slices the object. * * @param src is the base class object to copy from */ Derived(const Base &src); } // class Derived
I really like this idea. I will look into how feasible it is for ccdoc. The only issue is how well ccdoc can recognize these relationships.
|
Id: 0096
[Top] [Summary] [Next] [Previous] |
As you know, if you have a set of overloaded functions, they often perform essentially the same function, and could be described by the same ccdoc comment. Therefore, it would be handy to have a tag that would tell ccdoc to apply the comment attached to the previous method to the current method. For example:Solution Description:class MyClass { public: /** * Performs some useful function. blah blah blah. * Some long, involved explanation that I don't * want to repeat over and over again. * @param someVar Some user defined variable. * @returns Returns 0 if successful or not 0 otherwise. */ void myOverloadedFunct(A* someVar); /** @repeat */ void myOverloadedFunct(B* someVar); /** @repeat */ void myOverloadedFunct(C* someVar); /** @repeat */ void myOverloadedFunct(D* someVar); /** @repeat */ void myOverloadedFunct(E* someVar); }
This is an interesting suggestion. I believe that it relates to issues 0093 and 0094. They are all trying to address the problem of having to type redundant comments. I am actively looking into this.
|
Id: 0105
[Top] [Summary] [Next] [Previous] |
The javadoc @see tag defines the label part as optional: http://java.sun.com/j2se/1.4/docs/tooldocs/win32/javadoc.html#@seeSolution Description:If you decide to do the label optional, javadoc specifies how to generate the link: http://java.sun.com/j2se/1.4/docs/tooldocs/win32/javadoc.html#shortened
I don´t feel that this improvement is very important, but if you like the idea put it on the issue list and maybe implement it if there is time and no more important things to do.
|
Id: 0106
[Top] [Summary] [Next] [Previous] |
The javadoc spec for @see allows unqualified names as well as qualified names: http://java.sun.com/j2se/1.4/docs/tooldocs/win32/javadoc.html#seesearchorderSolution Description:I believe that ccdoc requires a fully qualified c++ class name if the reference is to another class. The javadoc spec relies on rules including the java compiler and I don´t believe that following their search patterns will add that much.
However, it would be nice if the @see tag accepts valid c++ class names. Leaving out the namespace part if the referencing class belongs to the same namespace as the referenced class would be an improvement.
I don´t think this improvement is very important since namespaces are quite short compared to java packages.
|
Id: 0108
[Top] [Summary] [Next] [Previous] |
I am playing around with the switches -rptdefasd, -rptdefsd and -rptdefv, the intention to mark the text red "Undocumented" in order to find missing documentation and implicitly defined member funcions.Solution Description:For the implicitly defined member funcions I want the text "Implicitly defined", and for things that I forgot to document "Undocumented", the switches used:
-rptdefasd "Implicitly defined" -rptdefsd "Undocumented" This causes the class documentation look just like I want. However, I have found the following errors:
The switches I am quite happy with: -rptdefa "Unascribed" -rptdefasd "Implicitly defined" -rptdefsd "Undocumented" -rptdefv "Unknown"
- -rptdefsd, documentation error, the default text is "undocumented", not "automatically generated". Documentation is exactly as for switch -rptdefasd, did you forget to edit after copy?
- -rptdefv, this switch sets the default text for missing version information, documentation says something completely different. Default text is "unknown", not "undocumented"
And some suggestions:
- The generated documentation has only one little problem, undocumented packages are reported "Implicitly defined" on the index pages. I suggest that you use the switch -rptdefsd instead of -rptdefasd for missing package documentation.
- In order to find all possible problems with the documentation, mark the text red
|
Id: 0113
[Top] [Summary] [Next] [Previous] |
On a related note, I would like to talk to you about possibly supporting a feature in ccdoc to merge databases. We could really parallelize the process if we were not limited to having every ccdoc process write to the same database. For example, run the header processing on each of the 50 or so subsystems individually in parallel, run a (new) step to merge the resulting databases into 1, and then do the index/html steps. Does ccdoc have any capability like this? If not, what do you think of this idea? In general, long processes that are single threaded are what kill our build times, and ccdoc (the way we use it) at ~1 hour is in that camp.Solution Description:
I think that this is very interesting idea that shouldn't be too difficult. I will look into it for the next release.
|
Id: 0119
[Top] [Summary] [Next] [Previous] |
In regards to issue 0115, replacing the backslashes with forward slashes in the -html switch does fix the problem. i.e. this worksSolution Description:But in DOS you can't use backslashes in the headers path. i.e. this doesn't workccdoc -db docs.db -html ./doc ..\Headers\*.hThis is not entirely satisfactory because now the two path descriptions in the command line need to be in different formats (the html path in UNIX form, the header path in DOS form). IMO the best fix would be to accept any paths in either format on any platform, but I have no idea how tricky that would be to implement.ccdoc -db docs.db -html ./doc ../Headers/*.h
I agree.Unfortunately this is somewhat difficult because ccdoc can't reliably determine whether it is running under cmd.exe or under a unix shell that is running on top of cmd.exe at run time.
If it is running under cmd.exe, it can safely replace backslashes with forward slashes but, if it is running under a unix shell that is running on top of cmd.exe it cannot.
I will look into whether ccdoc can be enhanced to allow you to specify UNIX style paths (with wildcards) under DOS.
|
Id: 0121
[Top] [Summary] [Next] [Previous] |
I use package names that reflect the directory structure. Each package is run through ccdoc in a separate ccdoc invocation.Solution Description:I use the same namespace between packages.
The namespace comment is valid only for the package in which it is defined. This is alright. The problem is that the documentation is a bit unclear about the scope for namespace doc comment reuse. I thought the namespace comment could be reused between packages.
This makes good sense. In fact, this is how it should work. I suspect that there is a bug in there somewhere.
|
Id: 0126
[Top] [Summary] [Next] [Previous] |
CcDoc does not recognize ccdoc c++ short comment in a "typedef struct" definition (comment does not appear).Solution Description:typedef struct { int foo1; //@- useful stuff 1 int foo2; //@- useful stuff 2 } MY_STRUCT;
|
Id: 0128
[Top] [Summary] [Next] [Previous] |
May the class content be sorted not just alphabetically, but separating method and attributes ? I think it would be more clear and efficient (as it was in ccdoc v0.7).Solution Description:
Some folks like it this way, others like it the way that you have described. I think that a switch should be added that allows this.
|
Id: 0131
[Top] [Summary] [Next] [Previous] |
I can't report this verbatim because my e-mail system crashed but here is my best recollection.Solution Description:Modify ccdoc to allow different comments for each item in a comma separated declaration. For example:
const string /** This is one string */ string1 = "aaa", /** This is another string */ string2 = "bbb";Note that you can workaround this in the near term as follows:// Workaround /** This is one string */ const string string1 = "aaa"; //** This is another string */ const string string2 = "bbb";
|
Id: 0132
[Top] [Summary] [Next] [Previous] |
Solution Description:Presently we have
string m_memberString; //@- the member documentation for this itemHow about adding this://@+ the member documentation for this item string m_memberString;
This makes sense as a shorthand but since you can workaround it by using the following syntax. This modification will be scheduled with a lower priority./** the member documentation for this item */ string m_memberString;
|
Id: 0137
[Top] [Summary] [Next] [Previous] |
The {@link} directive is not correctly recognized in @param clauses. The code example below demonstrates the problem:Solution Description:namespace testXX { class A { public: A(); ~A(); /** * Run the test using the specified arguments. * @param argc The number of arguments. * @param argv The collection of arguments. * @returns The run status. */ virtual int Run(int argc,char** argv); }; class test : public A { public: test(); ~test(); /** * Run the test using the {@link test99::A::Run} command. * @param argc The number of arguments {@link test99::A::Run}. * @param argv The collection of arguments {@link test99::A::Run}. * @returns The run status {@link test99::A::Run}. */ virtual int Run(int argc,char** argv); }; };
2002/12/06 jdl
After a bit more investigation, I found that there was a workaround as shown below which makes the fix less urgent.namespace testXX { class A { public: A(); ~A(); /** * Run the test using the specified arguments. * @param argc The number of arguments. * @param argv The collection of arguments. * @returns The run status. */ virtual int Run(int argc,char** argv); }; class test : public A { public: test(); ~test(); /** * Run the test using the {@link test99::A::Run} command. * @param argc The number of arguments * {@link test99::A::Run}. * @param argv The collection of arguments * {@link test99::A::Run}. * @returns The run status {@link test99::A::Run}. */ virtual int Run(int argc,char** argv); }; };
|
Id: 0143
[Top] [Summary] [Next] [Previous] |
"Javadoc documentation is great and very handy to use".Solution Description:
I agree. I will look into this in the near future. A couple of additional thoughts: 1) the new format should be switch controllable to avoid disrupting existing clients, 2) the new format may be higher priority than the XML release (this needs to be investigated).
|
Id: 0148
[Top] [Summary] [Next] [Previous] |
Have you thought, instead of giving color parameters etc. to ccdoc that it can make custom looking html you could give a custom tailored .css-file to ccdoc and it could generate html which uses a .css-file?Solution Description:
I have not thought about it because I don't know much about style sheets but it sounds like a good idea.
|
Id: 0151
[Top] [Summary] [Next] [Previous] |
Have you thought adding a change to translate contents of generated html? It would be nice, if ccdoc would read certain words like "class", "contents" etc. from a file which would be kind of resource file. If you change contents a file to f. ex. finnish, then you would have finnish documentation!Solution Description:The file (simple ex.) could have key-string pairs:
Then you could generate english documentation: ccdoc -langfile ccdoc.en.language ... and finnish: ccdoc -langfile ccdoc.fi.language ...file: ccdoc.en.language CLASS=class DESTRUCTOR=destructor file: ccdoc.fi.language CLASS=luokka DESTRUCTOR=tuhoaja
|
Id: 0165
[Top] [Summary] [Next] [Previous] |
Doxygen allows a virtual grouping of definitions that is very useful (@name and @{).Solution Description:
I am all in favor of making ccdoc and doxygen style comments compatible so that users can use both of them. This change is a challenge because the syntax conflicts with a current ccdoc usage. I will look into it when I can.
|
Id: 0167
[Top] [Summary] [Next] [Previous] |
I've found: another little problem. In German we write for the short form of "for example" "z.B.". ccdoc ignores silently the rest of the line after the B:Solution Description:--> in .h : /** das ist z.B. test */
--> in .html : "das ist z.B" without the last point an the rest of the line
Your example should work. This is a bug in ccdoc. It is supposed to terminate the short description after it encounters a dot that is followed by white space.For now you can workaround it by embedding trailing spaces before the last dot as shown below:
--> in .h : /** das ist z.B . test */
--> in .html : "das ist z.B. test"
Fixed in r39, verified by test 84.The short description string size was not updated properly in ccdoc::phase1::scanner_doc::add_line.
|
Id: 0175
[Top] [Summary] [Next] [Previous] |
Several users have requested a search capability in ccdoc. This capability can be provided in two ways: use a standard HTML based search engine on the generated files or provide a database of indexed items that can be used for a custom search engine. Although the standard HTML based search engine approach works well, there are still some users who would like a pre-generated index of searchable items.Solution Description:My current thinking about how to implement this is to provide a -rptidx switch for phase 3 that generates two HTML indices: one by name and one by entity type by name.
It would also generate an XML based index file that could be used by 3rd party search engines. Each XML record would contain the name of the entry (i.e. GetName), the type (method, function, etc.) and the associated URL. Multiple entries with the same name would be allowed.
It might also be desirable to have it contain information about the scope of the name. This means that each enclosing namespace and class might be specified as list. Consider the case of the X::Y::A::B::GetName() method where X and Y are namespaces, and B is a nested class inside of A. The scope information would contain data like
((nsp X) (nsp Y) (cls A) (cls B))
.Yet another desirable attribute would be to have this index view of the data tie in to the XML output that is being generated in the proto-type version for the next major release. In this release the database format will convert to XML. The phase 2 processing will generate fully cross-indexed entities that are used in phase 3 to generate HTML. Of course the user can stop at phase 2 and work directly with the XML. This is different than today where the database format is NOT XML and it is not fully cross referenced.
|
Id: 0176
[Top] [Summary] [Next] [Previous] |
This suggestion was actually made some time ago but I just got around to adding it.Solution Description:Ragav suggested that ccdoc be enhanced to support quick links to entries in the table of contents. The idea is that for tables with a large number of classes (or methods), the user could click on a quick link to the region they were interested in.
In practice it would look something like this:
I think that this would be very useful for large systems that have hundreds or even thousands of classes.Table Of Contents Quick Links: A B C D E F G H ... Z . . .
|
Id: 0177
[Top] [Summary] [Next] [Previous] |
I have found something wrong with my output though - not sure if this is a bug or if I am doing something wrong. I have the following (simplified) situation:Solution Description:You'll see that I have a member variable 'x' and also a member function which has 'x' in it's argument list. This causes CcDoc to place a link from the argument name 'x' to the documentation for the member variable 'x'.class SOMECLASS { public: void DoSomething(int x); int x; };I could simply change one of these names to ensure that there is no clash, however I thought you'd like this kind of problem submitted as a bug.
|
Id: 0178
[Top] [Summary] [Next] [Previous] |
I have a file as_config.h:Solution Description:#endif#ifndef AS_CONFIG_H #define AS_CONFIG_H namespace ApplicationInterface { enum { zero=0, one=1, two=2 }; enum { number_of_tasks = 60, task_stack_size = 4096, number_of_queues = 32, number_of_event_groups = 32, number_of_timers = 32 }; } // namespace ApplicationInterfaceThe problem is that I see two "$anonymous$" enum entities in the namespace page, but both link only to a page that lists only the first enum.
I know that in this case the enums could be combined, but this seems less than satisfactory as a general solution. Is this a known issue? (I couldn't find one that seemed to match on the ccdoc website) Is there interest in fixing this? I have no idea how hard it is to change this. Either way I likely will have to do something to at least address this in-house.(perhaps just complain when the second enum is detected...this would at least allow us to detect the problem and perhaps "invent" a name for at least one of them).
|
Id: 0179
[Top] [Summary] [Next] [Previous] |
It would be nice if ccdoc had a directive like "@entity <entityName>".Solution Description:Here is how I would want to use it. I have a macro which declares and defines a static member function. It is a macro because every class in the hierarchy should have this function (it returns an "interface ID"). The code currently looks like this:
It would be nicer if I could write://@{ // Returns the ID value for this interface. //@} Gf_DECL_INTERFACE_ID("{8DC3F1C6-F7CA-42d8-AFD6-7AF950C5C11E}") # if defined(__ccdoc__) static Gf_GUID InterfaceID(); # endif//@{ // Returns the ID value for this interface. // @entity static Gf_GUID InterfaceID() //@} Gf_DECL_INTERFACE_ID("{8DC3F1C6-F7CA-42d8-AFD6-7AF950C5C11E}")If there is already a way to do this, I'd appreciate hearing about it. If not, please consider adding a feature of this sort sometime in the future.
I can see where it would be useful to have real function name in the table of contents. The #ifdef workaround will certainly work until this issue is addressed.
|
Id: 0180
[Top] [Summary] [Next] [Previous] |
I've been happily using CCDOC for quite some time, but I'm stumped on how to control the length of the generated HTML filenames. I'm using nested packages and the generated filenames are so long that some filenames get truncated when written to a CD-ROM. This results in broken links when the documentation tree is copied from the CD-ROM.Solution Description:I've tried using the -maxpathlen option during the (-htm) output generation phase, but this doesn't shorten the filename length.
|
Id: 0181
[Top] [Summary] [Next] [Previous] |
I would like to report a minor issue with ccdoc, described below.Solution Description:The documents created by ccdoc indicate that a constructor will be generated automatically by the compiler even if one is specified. This seems to occur when the constructor for class X is specified as X(X const &), but does not occur when the constructor is specifed as X(const X &), although these are equivalent.
|
Id: 0182
[Top] [Summary] [Previous] |
I was going through the ccdoc for [one of my classes] this morning when I had a couple of feature ideas for ccdoc. I was thinking that it would be nice if overloaded methods in a class could be collapsed and expanded so that you only saw one line with the function name. If all of the short descriptions matched for the method, you could see the short description - if the short descriptions didn't match you would see something like "6 overloads". E.g. you might see something like this:Solution Description:When you click the + to expand you would see the display much like it is today with each overload displayed on its line with its short description. At this point the '+' would turn into a '-' and clicking the '-' would collapse it back down.+ GetClient Xdm_ObjectDerived method protected 16 overloaded methods (click '+' to expand)Extending that idea, I thought it would be nice if we had an extra tag that we could tag methods with to categorize them - E.g. "@category Debug Helper". All methods with matching categories would go into an expandable / collapsable grouping like the overloaded methods. (If there's just one or two methods in a category maybe it defaults to expanded but more than two and the category defaults to collapsed.) Methods with no category would be displayed as they are today without the expand/collapse capability. Methods within a category are alphabetized within the category. We would also probably want links at the top and bottom to 'expand all' and 'collapse all' - maybe throw in some cookies to store the user's preferences (overall and for each class) - hah.
Then I also thought that it was somewhat annoying with the ccdoc for some classes - Xdm_Model is one example - where there is a lot of good documentation for the class but it's a pain to have to scroll through all of that to try and find the method index so I can see if it supports the method I'm looking for. Having all of the class documentation and examples are great for the first time you're using a class but for quick reference which is most of the time after the first time you've used a class, all of that stuff gets in the way. If classes could have extra tags to separate things out like @Examples, @Extending, @UserGuide or something like that then in the ccdoc, you could either have expand / collapse things like above to expand out the examples text - or you could have links that take you to the examples for a specific class etc. I like the expand/collapse symbols just to keep everything on one page but it would take some javascript or something I'm sure and may be more pain than it's worth.
Anyway, these were just some pie in the sky ideas I had this morning. I didn't think them through a whole lot - just wanted to send them off to you to see if you liked them.
These are very interesting ideas. I will definitely look into them for future releases.


library(tidyverse)
library(cowplot)
library(broom)
library(modelr)
library(viridis)
library(lubridate)
library(hms)
library(knitr)
library(kableExtra)
knitr::opts_chunk$set(tidy.opts=list(width.cutoff=60),tidy=TRUE, echo = TRUE, message=FALSE, warning=FALSE, fig.align="center")
source("../../IDA/tools/plotting_tools.R")
theme_set(theme_1())
Ok, the idea for performing FRAP is that we could compare our echem estimates of \(D_{ap}\) / \(D_{phys}\) with a echem independent method. In this way we could perform echem and FRAP in vitro on reduced PYO samples to validate the echem method. This would be generally useful, but also could practically provide the foundation for a small paper we may try to submit in Sept.
This data is from my third time trying FRAP with PYO during the month of May.
For this iteration of FRAP, I loaded 3mL of molten agarose + PYO solution into mattek coverslip bottom dishes. PYO was ~ 300uM and I set up the experiment to compare 1% agarose to 4% agarose or 1% agarose + ctDNA (100uL of 5mg/mL solution). This setup worked quite well - I made stocks of 1% and 4% agarose (microwave) and then poured the molten solutions into 15mL falcon tubes and left those in the 60 degree water bath. Then I pipetted the required amount of PYO stock (~145uL) into another falcon tube (and ctDNA if needed) and then pipetted 3mL of the agarose stock into the tube. I vortexed then replaced the tube in the 60 degree bath for a minute. Finally, I could just pour directly from the tube into the dish and let it solidify (>10min).
At this point there should be no fluorescence in the agarose pad, because all of the PYO was oxidized. To reduce the PYO I have been using the reductant TCEP. At least 5 min before imaging I spotted 90uL of TCEP stock (~3mM final - a 10 fold excess over PYO) onto the center of the pad.
Before TCEP you can clearly see the blue - green oxidized PYO:
knitr::include_graphics("IMG_20190530_173653.jpg")
After TCEP addition, you can see that a clearish circle forms, which is the reduced PYO (which is fluorescent)
knitr::include_graphics("IMG_20190530_174341.jpg")
For the acquisition of the FRAP you can see the settings I used as images in this folder. Briefly, I used the HHMI 880 scope at the BIF with the 10x air objective. I zoomed into a 500um x 500um frame, which seemed to allow for faster scanning. I also used a very coarse pixel size (128px X 128px frame), so that the acquisitions could be taken very rapidly (~100ms). I setup my bleach area with the regions manager, and used the same area for all the acquisitions (100um diameter).
I used the 405nm laser to excite the PYO and collected all the light above that. It also helped that I could open the pinhole as wide as possible (~70um), since the internet suggested to open the pinhole to the scale of the structure being bleached…This allowed me to get nice fluorescent signal with relatively low laser power (3% ?) and gain (700?), which can be hard since PYO is a pretty weak fluor.
I took all of the FRAPS with pretty much the same parameters after playing around with the first sample. I did try a variety of bleaching times. Beyond 5 seconds there was no improvement in the bleach and shorter than that started to cause weaker bleaches (which were already very weak). Anyway, all of the data analyzed here was acquired with 20x of a speed 5 bleach.
You can see the more of dataset here: https://docs.google.com/spreadsheets/d/1yVjCwfowj3CKxjMnXN-0_H9WkdC7mJEGBxIwM22kokc/edit?usp=sharing
of course the raw czi files are still on the external harddrive as well.
As you can see in the FRAP analysis 1 file from today, I tried a lot of random stuff that did not work particularly well. I could sort of extract \(\tau_{1/2}\), but because the data are very noisy the trend looked real, but the values were much too short…So I did some reading and I found a lot of random mostly useless information about FRAP. But finally I ran across ‘easyFRAP,’ which actually discussed how it fits the data in simple terms and apparently it even uses the ‘nls’ function, which I like. So see the plan below.
Refs: easyFRAP, and the manual appendix which has the actual equations and good stuff.
Using their terminology here’s the plan:
Single exponential model:
\[ I_{fit} = I_0 - \alpha e^{- \beta t}\] for this model:
\[\tau_{1/2} = \frac{ln(2)}{\beta}\]
Ok let’s do this.
First, let’s read in the csv of the frap data. All of these scans were acquired in the same way, so they should be pretty comparable. In excel I already normalized the intensity values to the average baseline intensity (before bleach), so we already have the singly normalized data.
Here’s what the csv looks like.
df_single_norm <- read_csv("05_30_19_PYO_FRAP_data.csv") %>%
rename(single_norm_int = scaled)
df_single_norm
## # A tibble: 3,500 x 12
## img_num time intensity avg_baseline single_norm_int filename condition
## <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
## 1 1 0 0.032 127. 0.000251 1percen… 1percent…
## 2 2 0.208 128. 127. 1.00 1percen… 1percent…
## 3 3 0.399 128. 127. 1.01 1percen… 1percent…
## 4 4 0.591 126. 127. 0.993 1percen… 1percent…
## 5 5 0.782 127. 127. 0.994 1percen… 1percent…
## 6 6 0.974 127. 127. 1.000 1percen… 1percent…
## 7 7 1.17 127. 127. 1.000 1percen… 1percent…
## 8 8 1.39 127. 127. 0.997 1percen… 1percent…
## 9 9 1.58 127. 127. 0.998 1percen… 1percent…
## 10 10 1.77 128. 127. 1.01 1percen… 1percent…
## # … with 3,490 more rows, and 5 more variables: PYO <chr>,
## # objective <chr>, resolution <chr>, bleach <chr>, id <dbl>
And here’s a plot of that singly normalized dataset.
# ignore the two frames that have ~ 0 intensity (frame 1 and
# frame post bleach)
ggplot(df_single_norm %>% filter(intensity > 0.1), aes(x = time,
y = single_norm_int, color = condition, group = id)) + geom_line(alpha = 0.5) +
facet_wrap(~condition, scales = "free")
Now let’s perform the full scale normalization.
df_full_norm <- df_single_norm %>% filter(intensity > 1) %>%
group_by(condition, id) %>% mutate(min_single_norm_int = min(single_norm_int)) %>%
mutate(full_norm_int = (single_norm_int - min_single_norm_int)/(1 -
min_single_norm_int))
And plotting that looks like this:
ggplot(df_full_norm, aes(x = time, y = full_norm_int, color = condition,
group = id)) + geom_line(alpha = 0.5) + facet_wrap(~condition,
scales = "free")
Now let’s normalize the time to the first post_bleach frame time. Note that the bleaches all occur between 2-6 seconds for all of the conditions, so we can find that frame by searching for the minimum timed frame that exists greater than 5 seconds.
df_full_fit <- df_full_norm %>% filter(time > 5) %>% group_by(condition,
id) %>% mutate(min_time = min(time)) %>% mutate(norm_time = time -
min_time)
ggplot(df_full_fit, aes(x = norm_time, y = full_norm_int, color = condition,
group = id)) + geom_line(alpha = 0.5) + facet_wrap(~condition,
scales = "free")
Now we are finally read to fit! First, here’s a plot of what the fits will look like.
ggplot(df_full_fit, aes(x = norm_time, y = full_norm_int)) +
geom_point(shape = 21) + geom_smooth(method = "nls", formula = y ~
(I0 - a * exp(-B * x)), method.args = list(start = c(I0 = 0.85,
a = 0.5, B = 0.563)), se = F) + facet_wrap(condition ~ id)
Not too bad…seems pretty reasonable. Now let’s just perform the nls command and extract the coefficients in to a dataframe etc.
sing_exp_coef <- df_full_fit %>% group_by(condition, id) %>%
do(tidy(nls(formula = full_norm_int ~ I0 - a * exp(-B * norm_time),
data = ., start = list(I0 = 0.85, a = 0.5, B = 0.563)),
conf.int = T))
ggplot(sing_exp_coef, aes(x = condition, y = estimate, color = condition)) +
geom_pointrange(aes(ymin = conf.low, ymax = conf.high), position = position_jitter(height = 0)) +
facet_wrap(~term, scales = "free") + scale_color_viridis_d()
There are a few things to notice here. First, notice that the 95% confidence intervals are definitely the tightest for the 4% agarose condition. This makes sense, since we acquired the nicest FRAP curves with that conditions, and you can see that the nls fits those data quite well. Perhaps because the FRAPs were generally noisy, you can see that our parameter estimates definitely overlap between the different conditions for all the parameters (except maybe \(I_0\)). So already, I know that there’s know statistically significant difference between the conditions for \(\beta\), which will determine the \(\tau_{1/2}\).
But let’s go ahead and calculate \(\tau_{1/2}\) from \(\beta\) to see if the values make sense.
tau_est <- sing_exp_coef %>% filter(term == "B") %>% mutate(tau_mean = log(2)/estimate) %>%
mutate(tau_low = log(2)/conf.low) %>% mutate(tau_high = log(2)/conf.high)
ggplot(tau_est, aes(x = condition, y = tau_mean, color = condition)) +
geom_pointrange(aes(ymin = tau_low, ymax = tau_high), position = position_jitter(height = 0)) +
scale_color_viridis_d()
Overall, this estimate for \(\tau_{1/2}\) makes sense. It’s about what we could see by eye for these noisy curves…I think I estimated 5 seconds for my original FRAP acquisition.
For the DNA sample the first run (id = 1) seems to be an outlier. Let’s look at it compared to the next acquisition.
ggplot(df_full_fit %>% filter(condition == "1pctAgaroseDNA" &
id == 1), aes(x = norm_time, y = full_norm_int)) + geom_point(shape = 21) +
geom_smooth(method = "nls", formula = y ~ (I0 - a * exp(-B *
x)), method.args = list(start = c(I0 = 0.85, a = 0.5,
B = 0.563)), se = F)
ggplot(df_full_fit %>% filter(condition == "1pctAgaroseDNA" &
id == 2), aes(x = norm_time, y = full_norm_int)) + geom_point(shape = 21) +
geom_smooth(method = "nls", formula = y ~ (I0 - a * exp(-B *
x)), method.args = list(start = c(I0 = 0.85, a = 0.5,
B = 0.563)), se = F)
You can see that theres a weird grouping of points that do not decay following the bleach and the nls call basically ignores them giving a very shallow sloping curve. Because the recovery rises above 1 we know there was also significant TCEP equilibration happening, so I think we can safely throw this one away.
Looking at some of the other FRAP curves up close you can see the fits are ok, not great. They often don’t take into account the full decay including the first few points post bleach. There may be a difference with a different analysis scheme, but I’m happy with this for now.
ggplot(df_full_fit %>% filter(condition == "1percentAgarose" &
id == 15), aes(x = norm_time, y = full_norm_int)) + geom_point(shape = 21) +
geom_smooth(method = "nls", formula = y ~ (I0 - a * exp(-B *
x)), method.args = list(start = c(I0 = 0.85, a = 0.5,
B = 0.563)), se = F)
ggplot(df_full_fit %>% filter(condition == "4pctAgarose" & id ==
1), aes(x = norm_time, y = full_norm_int)) + geom_point(shape = 21) +
geom_smooth(method = "nls", formula = y ~ (I0 - a * exp(-B *
x)), method.args = list(start = c(I0 = 0.85, a = 0.5,
B = 0.563)), se = F)
Anyway, let’s end by calculating \(D_{ap}\) from our \(\tau_{1/2}\). Based on the expression from this paper we can calculate \(D_{ap}\) with the following expression:
\[D = 0.224 \frac{r^2}{\tau_{1/2}}\] where \(r\) is the nominal bleach radius from the ROI we bleached. I believe the ROIs were all approximately 50um or 0.005cm. Note that there are definitely biases in the assumptions for this model, but I’m going to use it for its simplicity now. (for example it assumes instantaneous bleach)
r = 0.005
d_est <- tau_est %>% mutate(d_mean = (0.224 * r^2)/tau_mean) %>%
mutate(d_low = (0.224 * r^2)/tau_low) %>% mutate(d_high = (0.224 *
r^2)/tau_high)
ggplot(d_est, aes(x = condition, y = d_mean, color = condition)) +
geom_pointrange(aes(ymin = d_low, ymax = d_high), position = position_jitter(height = 0)) +
scale_color_viridis_d()
These values are a little lower than expected, just barely in the \(10^{-6} cm^2 / sec\) range. Perhaps this error is due to the poor assumptions mentioned in the paper. Unfortunately it was not possible to correct using the effective bleach radius as they describe…
“Failure to take this into account can lead to underestimation of D when the Soumpasis or Axelrod equations are used to analyze confocal FRAP data, especially for fast diffusing soluble proteins”
Using this analysis scheme, I think we have a reasonable first pass quantifying the FRAP data. Unfortunately we don’t see evidence that the diffusion of the reduced PYO is different (slower) in high percentage agarose or agarose with DNA. That said, this is very good to know before starting the in vitro echem. Moving forward I have the highest confidence in the 4% agarose FRAP estimates, so that may be useful down the road. Now we need to think about what other perturbations we can perform in the agarose to perturb the diffusion of reduced PYO…
I think I have now optimized the acquisition settings as much as possible. We are acquiring as fast as possible, and we are sensitively measuring PYO fluorescence. The part of this FRAP that is still not great is the bleach. I can think of three reasons that may be:
Obviously these explanations lend themselves to different solutions, so in thinking of what to try next I would say we could: * Try FRAP with the adjustable 2-photon laser on the LSM-880, which can be adjusted down into the 300’s nm range. * Use electrochemically reduced PYO. Either generated from the survival chambers or in situ with the ITO coverslips I ordered.


KATE HANSEN
SKILLS
- HTML/CSS
- JavaScript
- Node.js
- JQuery
- SQlite
- Ruby/Ruby on Rails
- Materialize
- Git/Github
- Webflow
- Glitch.com
WEB
- Adobe Suite:
incl. Photoshop, Illustrator, Indesign - Affinity Suite:
incl. Photo, Design, Publisher
DESIGN
- Figma
- Balsamiq
- draw.io
- Clickup
- Asana
- Trello
PM + WIREFRAME
- Video editing + Production
- 3D Modeling including Blender, Sketchup


Summary
Here, I’ll review quickly and roughly how files work. Then I’ll show some experimental data to explain what are buffered reads and writes.

Review of file mechanics
When accessing a file, you typically obtain a sort of socket, or stream, or file descriptor, from which to read or write. Normally, a file has a beginning and an end.
In general, the process of dealing with files goes like this :
- Ask the OS to create or open a certain file. Depending on the programming environment, you will get various things that represent that file.
- Use that thing to write and read from the file in question.
- You can read and write in two fashion:
- Sequentialy : you start from the beginning and you carry on until the end.
- Randomly : you start from anywhere and jump to anywhere into the file.
- When you’re done, tell the OS that you wish to close that file.
Now, let’s see that in code. Here’s a pseudo workflow (imagine that I’m handling the errors), starting by opening the file:
file, err := os.Open("myfile.data") // or os.Create(...)
// Sequential access operations
myData := make([]byte, dataLen)
n, err := file.Write(myData)
yourData := make([]byte, dataLen)
m, err := file.Read(yourData)
// Random access operations
var offset int64 = 42
myData := make([]byte, dataLen)
n, err := file.WriteAt(myData, offset)
yourData := make([]byte, dataLen)
m, err := file.ReadAt(yourData, offset)
We will not concern ourselves with random accesses for this post, because they come with their own performance issues.
You notice that each time we Read
or Write
with a file, we need to provide a piece of memory which the file will read from or write to. Oddly, the methods are taking arrays of bytes []byte
. That implies that somehow, we need to create a []byte
of some size. But what size? What if you don’t know the size of what you’re about to read? Can you just pass an array of size 1 and access every part of the file one byte at a time? Sure you can do that, but it might be a bad idea.
yourData := make([]byte, 1)
n, err := file.Read(yourData)
// do something with yourData[0]
Alright, so if it’s a bad idea, how do we read a file for which we don’t know the size in advance? What if I read the next 4 bytes but there were only 2 bytes left to read?
Let’s remember how we read some part of file
into yourData
:
n, err := file.Read(yourData)
// do something with yourData[:n]
In Go, yourData[:n]
means everything in yourData
up to n - 1
. Notice that we don’t use all of yourData
. We only use how much Read
said was put into yourData
. That is, n
bytes.
That’s much more burden onto you, isn’t it? Now you need to remember not to use all of yourData
but only its n
th first values. Why would you do that?
Performance!
Alright, alright. “Premature optimization is the root of all evil.” Sure it is, but in our case, it’s not so much premature. Disk IO is one of the most expensive thing your computer does. Like, orders of magnitude more expensive than any other operation on your machine… aside perhaps network accesses. So doing your file access right is kind of essential if you want to have somehow acceptable performance.
So, why should we read many bytes at once instead of one at a time? Let’s look at this graph, measured on my MacBook Pro1:
In the above graph, we see that as $access\ size$ increases, the time it takes to read a 1 MB file decreases. And this decrease is exponential (see the two logarithm scales).
So this is on my fast SSD. But your regular, cheap-o web instance won’t have a fast SSD (or most likely not), so what will performance look like? Well, even worst ! Let’s look at the same benchmark run on an AWS EC2 t1.micro instance:
You can see that for accesses using small buffers, the decrease in performance is 10 times that of my laptop SSD, while with buffering, the difference is not as significant (although the instance’s disk - an EBS - has pretty terrible performances).
So, I hope you’re convinced now of the importance of doing buffered disk accesses.
Sum It Up
How to do buffered reads and writes (ignoring all error handling):
buf := bytes.NewBuffer(nil)
// Choose a decent size, the Go standard lib defines bytes.MinRead
pageSize := bytes.MinRead
data := make([]byte, pageSize)
n, _ := file.Read(data)
m, err := buf.Write(data[:n])
if err == io.EOF {
// We're done reading, buf contains everything
}
Here I only handle err
to check for the io.EOF
that is returned when there is no more data to be read from the file. When you implement your own file logic in Go, do three things:
- Handle all of your errors
- If you just want all the bytes, use
ioutil.ReadAll(...)
- If you want to do something in real time with the data (decode its JSON content, gunzip it on the fly, …), don’t consume the actual data byte by byte. Instead, chain it with decoders:
file, err := os.Open(...)
gzRd, err := gzip.NewReader(file)
jsonDec, err := json.NewDecoder(gzRd)
for {
err := jsonDec.Decode(&yourStruct)
if err == io.EOF {
// We're done
}
}
In three line, you made an on-the-fly gzip JSON decoder. Go is pretty awesome.
Run It Yourself
The code to generate these graph is on my Github.
Please note that the code is written in a script-alike way with very bad error handling. Also, the IO code is pretty weird because of the need to compute time measurements and artificially write with various buffer size.
This is not idiomatic, good Go code.
Next
Right now, I don’t have a post on the random access side of the picture. The day might come where I will write such a post.
1: Retina, Mid 2012 with 256GB SSD


Shipwreck found off Sweden could be 17th century warship
by ReutersTwo ships, of which one is thought to be a sister ship to the Swedish warship Vasa, were found in the archipelago near Stockholm this week.
The ship from the 17th century was found in a strait near Vaxholm on Tuesday (November 5).
Marine archaeologist Jim Hansson who found the war ship, said divers found the wreck in an area where several vessels are known to be at the bottom of the sea.
Hansson said samples had been taken from the ship that he hoped would give more clues about which ship it may be.
A warship in the Swedish navy, Vasa sank in 1628 after setting sail on its maiden voyage. It is now in a museum after being salvaged in 1961 after 333 years at the bottom of the sea. Vasa is considered the world's best preserved 17th century ship.


1 The following was how he scheduled his affairs: in the morning, until the the hour when the marketplace filled, he readily conducted whatever business was brought to him; the rest of the day, he drank and joked at the expense of his companions and was idle and playful.
2 But this displeased his friends, who admonished him thus: “O King, you do not conduct yourself well by indulging too much in vulgarity. You, a celebrated man, ought to conduct your business throughout the day, sitting on a celebrated throne; and thus the Egyptians would know that they are governed by a great man, and you would be better spoken of; as it is, what you do is by no means kingly.”
3 But he answered them like this: “Men that have bows string them when they must use them, and unstring them when they have used them; were bows kept strung forever, they would break, and so could not be used when needed.
4 Such, too, is the nature of man. Were one to be always at serious work and not permit oneself a bit of relaxation, he would go mad or idiotic before he knew it; I am well aware of that, and give each of the two its turn.” Such was his answer to his friends.


| Field Summary | ||
|---|---|---|
const Number | DEFAULT | |
const Number | USA | |
| United States | ||
const Number | CANADA | |
| Canada | ||
const Number | LATIN_AMERIC | |
| Latin America, except Brazil | ||
const Number | RUSSIA | |
| Russia | ||
const Number | EGYPT | |
| Egypt | ||
const Number | GREECE | |
| Greece | ||
const Number | NETHERLANDS | |
| Netherlands | ||
const Number | BELGIUM | |
| Belgium | ||
const Number | FRANCE | |
| France | ||
const Number | SPAIN | |
| Spain | ||
const Number | HUNGARY | |
| Hungary | ||
const Number | ITALY | |
| Italy | ||
const Number | SWITZERLAND | |
| Switzerland | ||
const Number | AUSTRIA | |
| Austria | ||
const Number | UNITED_KINGDOM | |
| United Kingdom | ||
const Number | DENMARK | |
| Denmark | ||
const Number | SWEDEN | |
| Sweden | ||
const Number | NORWAY | |
| Norway | ||
const Number | POLAND | |
| Poland | ||
const Number | GERMANY | |
| Germany | ||
const Number | MEXICO | |
| Mexico | ||
const Number | BRAZIL | |
| Brazil | ||
const Number | AUSTRALIA | |
| Australia | ||
const Number | NEW_ZEALAND | |
| New Zealand | ||
const Number | THAILAND | |
| Thailand | ||
const Number | JAPAN | |
| Japan | ||
const Number | SOUTH_KOREA | |
| SouthKorea | ||
const Number | VIET_NAM | |
| Viet Nam | ||
const Number | CHINA | |
| People's Republic of China | ||
const Number | TURKEY | |
| Turkey | ||
const Number | INDIA | |
| India | ||
const Number | ALGERIA | |
| Algeria | ||
const Number | MOROCCO | |
| Morocco | ||
const Number | LIBYA | |
| Libya | ||
const Number | PORTUGAL | |
| Portugal | ||
const Number | ICELAND | |
| Iceland | ||
const Number | FINLAND | |
| Finland | ||
const Number | CZECH | |
| Czech Republic | ||
const Number | TAIWAN | |
| Taiwan | ||
const Number | LEBANON | |
| Lebanon | ||
const Number | JORDAN | |
| Jordan | ||
const Number | SYRIA | |
| Syria | ||
const Number | IRAQ | |
| Iraq | ||
const Number | KUWAIT | |
| Kuwait | ||
const Number | SAUDI | |
| Saudi Arabia | ||
const Number | UNITED_ARAB_EMIRATES | |
| United Arab Emirates | ||
const Number | ISRAEL | |
| Israel | ||
const Number | QATAR | |
| Qatar | ||
const Number | IRAN | |
| Iran |
| Field Detail |
|---|
DEFAULT | |
const Number DEFAULT |
USA | |
const Number USA |
CANADA | |
const Number CANADA |
LATIN_AMERIC | |
const Number LATIN_AMERIC |
RUSSIA | |
const Number RUSSIA |
EGYPT | |
const Number EGYPT |
GREECE | |
const Number GREECE |
NETHERLANDS | |
const Number NETHERLANDS |
BELGIUM | |
const Number BELGIUM |
FRANCE | |
const Number FRANCE |
SPAIN | |
const Number SPAIN |
HUNGARY | |
const Number HUNGARY |
ITALY | |
const Number ITALY |
SWITZERLAND | |
const Number SWITZERLAND |
AUSTRIA | |
const Number AUSTRIA |
UNITED_KINGDOM | |
const Number UNITED_KINGDOM |
DENMARK | |
const Number DENMARK |
SWEDEN | |
const Number SWEDEN |
NORWAY | |
const Number NORWAY |
POLAND | |
const Number POLAND |
GERMANY | |
const Number GERMANY |
MEXICO | |
const Number MEXICO |
BRAZIL | |
const Number BRAZIL |
AUSTRALIA | |
const Number AUSTRALIA |
NEW_ZEALAND | |
const Number NEW_ZEALAND |
THAILAND | |
const Number THAILAND |
JAPAN | |
const Number JAPAN |
SOUTH_KOREA | |
const Number SOUTH_KOREA |
VIET_NAM | |
const Number VIET_NAM |
CHINA | |
const Number CHINA |
TURKEY | |
const Number TURKEY |
INDIA | |
const Number INDIA |
ALGERIA | |
const Number ALGERIA |
MOROCCO | |
const Number MOROCCO |
LIBYA | |
const Number LIBYA |
PORTUGAL | |
const Number PORTUGAL |
ICELAND | |
const Number ICELAND |
FINLAND | |
const Number FINLAND |
CZECH | |
const Number CZECH |
TAIWAN | |
const Number TAIWAN |
LEBANON | |
const Number LEBANON |
JORDAN | |
const Number JORDAN |
SYRIA | |
const Number SYRIA |
IRAQ | |
const Number IRAQ |
KUWAIT | |
const Number KUWAIT |
SAUDI | |
const Number SAUDI |
UNITED_ARAB_EMIRATES | |
const Number UNITED_ARAB_EMIRATES |
ISRAEL | |
const Number ISRAEL |
QATAR | |
const Number QATAR |
IRAN | |
const Number IRAN |


Drive:
Drive
is about a man that is a wheelman for some bad people and trying to get out of
the life. It has high speed chases and awesome music.
Daft Punk:
Hot, funky, electronic beats by two robots.


Name: The Tor Project
Username: torproject
Verified: True
Contributors: False
Location: Everywhere
Followers: 267379
Description: We advance human rights and defend your privacy online through free software and open networks.
Url: https://t.co/ITPmX7EsMR
Name: TechRepublic
Username: TechRepublic
Verified: True
Contributors: False
Location: USA | UK | Australia | Asia
Followers: 205070
Description: Empowering the People of Business and Technology
Url: https://t.co/sUWeP57PsU
Name: Guido van Rossum
Username: gvanrossum
Verified: True
Contributors: False
Location: San Francisco Bay Area
Followers: 124913
Description: Python's creator and emeritus BDFL. Working at Dropbox. Computer History Museum Fellow.
Url: http://t.co/jujQDNMiBP
Name: Javascript Digest
Username: javascriptd
Verified: False
Contributors: False
Location: Everywhere
Followers: 26898
Description: Javascript Digest brought to you by http://t.co/rHlyfe7p7r
Url: None
Name: Grady Booch
Username: Grady_Booch
Verified: True
Contributors: False
Location: Maui
Followers: 25797
Description: scientist, storyteller, philosopher
Url: https://t.co/FoLx8K2rs7
Name: John Lambert
Username: JohnLaTwC
Verified: False
Contributors: False
Location: Redmond WA
Followers: 20163
Description: Distinguished Engineer, Microsoft Threat Intelligence Center, johnla(AT)https://t.co/WZyrx8JQ5q, https://t.co/n25Lzl2dIL
Url: https://t.co/FmulbJGxnz
Name: Wen👩🏻💻
Username: DD_Wen_
Verified: False
Contributors: False
Location: Melbourne & Sydney
Followers: 14996
Description: data scientist @DATAFYiNG. i do = data science + ai + lifelong learning + shopping + traveling + many more ya
Url: https://t.co/VS87FlFbv0
Name: What's Trending Now in 2018
Username: greatnews2017
Verified: False
Contributors: False
Location: New York, NY
Followers: 12992
Description: Award-Winning #Entrepreneur, Mommy, #Founder @greatnews2017 + Author, #SocialStar #OnlineCourses #trendingideas #whatstrendingnow
Url: https://t.co/6CMbkoRVXV
Name: CyberWar Books
Username: CyberToolsBooks
Verified: False
Contributors: False
Location: Cyber Domain
Followers: 9755
Description:
Url: http://t.co/3tWphGwfDq
Name: OccuWorld ❌
Username: OccuWorld
Verified: False
Contributors: False
Location: Earth
Followers: 7906
Description: Truth Locating Engine. Non-funded, volunteer, automated. In solidarity with humanity.
Url: https://t.co/ayBYM1vTQo


Getting Started¶
Welcome to Mysterious Messenger! There are a lot of features to try out and different ways to use the program. This page will walk you through the steps to set up Mysterious Messenger and begin using it, as well as explaining some of the available features and what you can create with this program.
Setting Up¶
If you want to run this code, you will need to download the Ren’Py engine: https://www.renpy.org/
As of 2022-09-15 (the time of this update) the version of Ren’Py used is 8.0.3. Mysterious Messenger is also intended to be compatible with 7.5+, though it’s recommended you use v8.0+ to take advantage of Python 3’s capabilities.
Download or clone the most recent release into your Ren’Py Projects Directory and unzip it into its own folder. If you don’t know what it is, you can change it from the Ren’Py launcher via
preferences -> Projects Directory
.The current stable version is 3.3.0
Refresh your Projects list and you should see
mysterious-messenger
listedSave the Script Generator spreadsheet somewhere you can find it later
Join the Mysterious Messenger Discord for update announcements and help with the program.
The images and sound files used in this project are not included in the repository. Please contact me directly if you would like to request the assets for personal use.
Getting the Program Running¶
If you’ve set up everything properly, from the Ren’Py Launcher you should be able to select the project you created from the column on the left and hit ‘Launch Project’. If you don’t have the images/sound files, you will likely run into several “not found” errors until you’ve created your own replacements; otherwise, you should be able to go ahead and type in your desired name for the protagonist and check out the “Tutorial Day”, which will walk you through some of the available features in the program.
Useful Built-in Features¶
Besides just modifying the code, the program has some extra features built into the Settings screen specific to this program. Many are useful when creating new content.
Accessibility Options¶
On the Preferences screen there are several toggles under the header Accessibility Options. These are explained below:
- Hacking Effect
Turns on/off the various flashing and glitchy “hacked” animations in the program.
- Screen Shake
Turns screen shake on/off.
- Chatroom Banners
Turns on/off animation for banners during chatrooms.
- Timed Menus
If turned off, Timed Menus will function like regular menus, with the chat stopping to show the player an Answer button before proceeding.
Also see the Timed Menu Speed slider, explained below.
- Animated Icons
Turns heart icon and hourglass animations into small text notifications. Note that you can also choose not to award hourglasses in the chatroom at all by unchecking Receive Hourglasses in Chatrooms in the Developer settings.
- Dialogue Outlines
Adds outlines to fonts during Story Mode, phone calls, and chatrooms to make them more readable.
Under the header Other Settings are a few additional sliders and options:
- Timed Menu Speed
If Timed Menus are turned on, this slider will adjust the amount of time the player is given to answer a timed menu. Moving this slider to the left will make messages post slowly, giving the player time to read them and decide on an answer before the timer runs out, and moving the slider to the right will cause messages to post very quickly, resulting in a shorter timer.
By default, this is set to the equivalent of chat SPEED 5. The value of this slider is not affected by how fast your chatroom speed is – you could have the chatroom regularly operate at SPEED 9 but timed menus slow the chat speed down to approximately SPEED 4 until the player either chooses an answer or the timer runs out.

- VN Window Opacity
Adjusts the opacity of the dialogue window during Story Mode (VN) sections of the game. If the slider is all the way to the left, the dialogue window will be completely transparent. Putting the slider all the way to the right will make the dialogue window completely opaque. This can be used in combination with Dialogue Outlines to make text easier to read.
- Background Contrast
Adjusts the opacity of the starry night background used in most menu screens. Dragging the slider all the way to the right makes the background completely black.
Finally, under the Sound tab in the Settings is an option for audio captions.
- Audio Captions
If checked, the program will display a notification describing background music and sound effects briefly when the sound is first played.
Dialogue Settings¶
There are several options which affect dialogue display on the Preferences tab:
- Text Speed
Affects how fast the text displays during Story Mode and phone calls. By default, the slider is all the way to the right, which means dialogue shows up instantly. Moving the slider farther to the right decreases the number of characters per second (CPS) that are shown, so dialogue will show up character-by-character instead of all at once.
- Auto-Forward Time
This program includes an “Auto” feature for Story Mode as well as phone calls. Setting this slider farther to the right results in a shorter delay between showing new lines of dialogue, and setting it farther to the left gives you more time to read dialogue before the program moves on to the next line.
- Skip Unseen Text
By default, this option is checked. If unchecked, the program will stop skipping/stop Max Speed when it comes across text you’ve never seen before. The program remembers which text you have seen across playthroughs.
- Skip After Choices
By default, this option is checked. If unchecked, the program will stop Max Speed/skipping after you make a choice, and you will need to press the Skip/Max Speed button again to resume skipping after a choice.
- Skip Transitions
If checked, this causes the program to not show transitions when skipping.
- Indicate Past Choices
If checked, the program will display a small checkmark in the corner of choices you’ve picked during past playthroughs.
Other Settings¶
- Modified UI
If checked, the program will change some of the UI elements in the game to be more consistent with the turquoise and black colour scheme found elsewhere in the game. It also includes some subtle animation for the choice screens. Does not affect gameplay in any way.
- Animated Backgrounds
If checked, the chatroom background will be animated. This often includes features such as gently drifting clouds or twinkling stars. The animations take 2-3 minutes to play out fully and then loop for the rest of the chatroom.
Developer Settings¶
Finally, there are several settings which are helpful when testing a route. They can be found both on the main menu and on the in-game home screen under the button labelled Developer.
- Testing Mode
When Testing Mode is turned on, you will have access to several useful features and conveniences:
Shows a button to instantly end a chatroom and mark it as played
Allows you to right-click any timeline item to instantly mark it as played
Removes several confirm-style messages (such as showing you how many missed calls and unread messages you have when loading a game, or asking for confirmation when proceeding through a plot branch)
Unlocks all available profile pictures for both the player and the characters
Delivers any outstanding email replies after proceeding through a single timeline item
Allows you to replay timeline items (such as chatrooms) multiple times and choose different options (otherwise, re-entering a played chatroom would just show a replay of the original conversation)
Any content in the
after_
label of a timeline item is also delivered each time you play the chatroom (rather than only the first time)
- Unlock all story
When checked, this option will make all timeline items available to play instantly, rather than having to play each item sequentially from beginning to end. This may result in failed plot branch checks, as available timeline items are not marked as played unless you’ve actually gone through them, but you are allowed to proceed through a plot branch at any time.
To get around this, you can include a check for if persistent.unlock_all_story in your plot branch label to branch the story differently when this option is checked.
- Real-Time Mode
By default, this is unchecked. Timeline items will appear sequentially one after the other after the previous item is played. However, if Real-Time Mode is checked off, then chatrooms and other content will appear at the scheduled time in real-time.
- Hacked Effect
This turns on/off the messenger “hacked” effects. This particular variable only affects the save file it is activated from, so it is not available to toggle from the main menu (only the in-game developer menu). It causes the timeline items to appear “glitchy” and changes the music on the main menu screen. Some of these glitch effects will not appear for players with the Hacking Effect option turned off.
- Receive Hourglasses in Chatrooms
Unchecking this option will stop awarding the player hourglasses during chatrooms. Currently hourglasses are awarded on a pseudo-random basis when a character posts a special speech bubble from a subset of special bubbles. This option is useful if the chatroom is intended to be seen as a video and not played through, for example.
- Use custom route select screen
Checking this option will cause the program to use the screen titled
custom_route_select_screen
instead ofroute_select_screen
when the player chooses a route at the start of the game. See Customizing the Route Select Screen for more.- Prefer local documentation
Checking this will cause the script error popup links to open the locally-saved html documentation that comes with the repository, where possible. If a file cannot be found, it will be opened in a web browser instead. This can be useful if you are working offline.
- Use pause footer for links
This will use the pause footer at the bottom of the screen while waiting for the player to click a link in the chatroom. By default, the game shows a customizable message that tells the player to click the link. See Stopping the Chat for more information.
- Fix persistent
This is an option primarily intended for users updating Mysterious Messenger from older (<2.0) versions to fix issues with saved persistent values. If Ren’Py is complaining about compatibility issues with persistent variables, you can try using this option to fix it.
- Documentation
Clicking this will open the documentation home page for Mysterious Messenger. By default, this opens the documentation in a web browser. However, you can check off Prefer local documentation (above) to open the html files that come with the repository instead. Both the online and offline versions contain the same information.
- Reset Albums
This will cause the program to forget all persistent variables associated with albums. This includes all the persistent albums as defined in the
all_albums
variable, and will also clear the program’s memory of images the player has been shown in-game (typically this will only affect CG images shown during Story Mode). Use this if you want to reset the persistent albums to their original, defaulted value.This option is only available on the main menu (not in-game).
- Chatroom Creator
This will open a sub-menu to create a new chat or load an existing one. You can use the chatroom creator to visually put together chatrooms and then watch them play out in-game or export them as code to put into the program.
Updates¶
By default, Mysterious Messenger will check for updates to the program once a day. You can customize this in several ways by clicking the update icon in the bottom-right corner of the main menu.
- Check for updates (once per day)
Unchecking this will stop the program from automatically checking for updates. You can still manually check for them with the Check for updates button.
- Check for prereleases
Checking this will include prereleases when the program checks for updates, and will inform the player if a recent prerelease is available. Unchecking this will cause updates to only search for complete releases.
- Ignored releases
Whenever the program finds a release you haven’t updated to yet, you’ll get the option to ignore it. Ignoring a release means that you won’t receive any more popups informing you of this particular update version. If you’ve ignored any versions, they will appear listed under the Check for prereleases option. You can uncheck any of these releases to stop ignoring them.
- Check for updates
Clicking this button will cause the program to manually check for any updates that fit the conditions you’ve specified above. You will receive a popup if it finds a new version you can update to.
Note
Mysterious Messenger requires an internet connection to check for updates. If it cannot connect to the internet and automatic updates are turned on, it will simply silently fail to fetch them (with no adverse effects to the rest of the program).
If Mysterious Messenger finds an update for you, you will see information such as the version number, publish date, whether or not it’s a prerelease, and a download link for the new code files. You can also choose to ignore this release. Your current program version will be displayed in the bottom-left corner of the popup.
Uncategorized¶
This section is for tips or program features that don’t have a particular category, but may be useful to players.
The space bar will activate most of the main buttons in a chatroom. It will switch between play/pause, click the Answer button when it is available, activate the Save & Close button, and click “Sign” on the signature screen.
The left/right keyboard keys will decrease/increase the chat speed during a chatroom.
You can swipe photos in a CG album to view the next or previous image without returning to the selection screen. It will automatically skip locked photos.
You can see the heart points you’ve earned with each character on your character’s profile screen
If you are on Windows, there is a file called
file_picker.exe
which is included in the assets. This will allow the game to display a file picker and let the player choose an image from their computer to use as a profile picture. It does not currently work for other platforms.
Program Features¶
Finally, below is an overview of the various features available within Mysterious Messenger:
Fully-featured chatrooms
Banners, emojis, screen shake
“Hacked” effects (e.g. screen tear)
Special fonts and bubbles
Animated chatroom backgrounds (optional; activated in settings)
Heart icons
Optional timed menus
Messages with clickable links
Create-a-chatroom feature to visually put together chatrooms and watch them play out in-game or export them as code to use in the program
Phone calls
- Calls can become available after each timeline item for the player to call the characters
The dialogue can change depending on whether the player is calling back a missed phone call or not
Characters can call the player
Story Calls, which are a mandatory part of the story on the timeline
Voicemail for when a character doesn’t pick up
Special callbacks that trigger when the player hangs up in the middle of a call
Text messages
Characters can send text messages after any timeline item
Schedule text messages to deliver at a particular time, or a time relative to other timeline items
Send emojis and CGs through text
Have text message conversations play out in real-time like a one-on-one chatroom
Create text message backlog before a route begins
Story Mode (VN)
Includes all the main characters’ outfits and expressions
Jump to Story Mode in the middle of a chatroom for flexible storytelling opportunities
Emails
Automatically unlock guests in the guestbook when you first invite them and when they attend the party
Flexible email chains of any length
Allows for “recovery” emails where the player made the wrong choice but can continue to email the guest to improve their chances of attending
Successfully invited guests are showcased before the party
Receive an hourglass for viewing a character in the guestbook for the first time
Routes
Set up plot branches during the story or when the player enters the party
Mix and match chatrooms, standalone story mode, and story calls
Include different Good/Bad/Normal ends (or your own kind of end!)
Previously played story is automatically unlocked in the History screen
Other features
Unlock CG images for a character’s album
Use pinch-zoom and other touch gestures to view CG images in the album
Profile picture callbacks to have the characters react to the player’s profile picture choices
Spaceship thoughts and occasional prizes from the Honey Buddha chip bag
Customizable pronouns (she/her, he/him, they/them) and gender (male/female/nonbinary)
Select a chatroom username separate from your regular name
Custom ringtones
Character greetings on the main menu
Real-time and sequential mode
Get input from the player for more detailed information
Support for multiple screen sizes
Custom splash screen
Custom in-game achievement system


This library has a dependency on the nblas package for fast matrix operations.
It should work by default on OSX, but on Linux you may need to run apt-get install libblas-dev
first.
On Windows you may need to install LAPACK.
This library is in an early development phase and many breaking changes are to be expected.
The TypeScript source files can be found on GitHub and the JavaScript production files(including .ts.d files) can be found as an npm package.
Below are some simple code usage examples. TypeDocs for all classes can be found here.
import * as ml from 'machine-learning';
// Feedforward neural network: solve XNOR problem (opposite of XOR)
const inputs = new ml.Matrix([[0, 0], [0, 1], [1, 0], [1, 1]]);
const targets = new ml.Matrix([[1], [0], [0], [1]]);
const feedforwardNeuralNetwork = new ml.FeedforwardNeuralNetwork([2, 5, 1], 0);
feedforwardNeuralNetwork.setNumberOfEpochs(1000);
feedforwardNeuralNetwork.setLearningRate(1);
feedforwardNeuralNetwork.train(inputs, targets);
const predictions = feedforwardNeuralNetwork.predict(inputs);
console.log(predictions.toArray());
// [ [ 0.9943559154265011 ], [ 0.012148393118769857 ], [ 0.013640408487437417 ], [ 0.9816837627444868 ] ]
For more detailed information, access the FeedforwardNeuralNetwork class documentation
import * as ml from 'machine-learning';
// Linear Regression: y = 1000 + 200 * x
const inputs = new ml.Matrix([[5], [7], [9], [11], [13]]);
const targets = new ml.Matrix([[2000], [2400], [2800], [3200], [3600]]);
const linearRegression = new ml.LinearRegression();
linearRegression.setNumberOfEpochs(10000);
linearRegression.setLearningRate(0.02);
linearRegression.train(inputs, targets);
const predictions = linearRegression.predict(inputs);
console.log(predictions.toArray());
// [ [ 1999.999991189672 ], [ 2399.9999948012005 ], [ 2799.999998412729 ], [ 3200.0000020242574 ], [ 3600.000005635786 ] ]
For more detailed information, access the LinearRegression class documentation
import * as ml from 'machine-learning';
// Logistic Regression: determine if second input is higher than first input
const inputs = new ml.Matrix([[1000, 1100], [4500, 3000], [700, 1300], [1150, 700], [1300, 1200], [600, 650]]);
const targets = new ml.Matrix([[1], [0], [1], [0], [0], [1]]);
const logisticRegression = new ml.LogisticRegression();
logisticRegression.setNumberOfEpochs(1000);
logisticRegression.setLearningRate(0.01);
logisticRegression.train(inputs, targets);
const predictions = logisticRegression.predict(inputs);
console.log(predictions.toArray());
// [ [ 1 ], [ 0 ], [ 1 ], [ 0 ], [ 0 ], [ 1 ] ]
For more detailed information, access the LogisticRegression class documentation
import * as ml from 'machine-learning';
// Multiclass Logistic Regression: determine the highest value
const inputs = new ml.Matrix([[4500, 1200, 3000], [700, 890, 800], [700, 1200, 1300], [1150, 600, 700], [600, 1500, 1650], [400, 401, 400]]);
const targets = new ml.Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1], [1, 0, 0], [0, 0, 1], [0, 1, 0]]);
const multiclassLogisticRegression = new ml.MulticlassLogisticRegression();
multiclassLogisticRegression.setNumberOfEpochs(10000);
multiclassLogisticRegression.setLearningRate(0.1);
multiclassLogisticRegression.train(inputs, targets);
const predictions = multiclassLogisticRegression.predict(inputs);
console.log(predictions.toArray());
// [ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ], [ 1, 0, 0 ], [ 0, 0, 1 ], [ 0, 1, 0 ] ]
For more detailed information, access the MulticlassLogisticRegression class documentation
import * as ml from 'machine-learning';
// Nearest neighbors: Equidistant examples, breaks ties by considering multiple neighbors even though number set to 1
const inputs = new ml.Matrix([[0, 0], [0, 1], [1, 0], [1, 1], [1, 1], [2, 2]]);
const targets = new ml.Matrix([[0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]]);
const nearestNeighbors = new ml.NearestNeighbors(inputs, targets);
nearestNeighbors.setNumberOfNeighbors(1);
const unknowns = new ml.Matrix([[0.5, 0.5], [1.5, 1.5], [1.75, 1.75]]);
const predictions = nearestNeighbors.predict(unknowns);
console.log(predictions.toArray());
// [ [ 0.4, 0.2, 0.2, 0.2 ], [ 0.6666666666666666, 0, 0, 0.3333333333333333 ], [ 0, 0, 0, 1 ] ]
For more detailed information, access the NearestNeighbors class documentation
Generated using TypeDoc


Hard work and success are inseparable. Believing in the above to put our hard work into the tasks at hand to achieve success. This is what gets this team going.
We as a team have done many projects and some of them have been uploaded to GitHub.
Before the team incepted formally, it was actually the workshop Git Init, conducted by the four of us in collaboration with CSEA which gave us the idea and vision of what we are now - Team Enigma
For the social cause of having a library for the CEG Hostels under Mr. G. Manikandan we created the software required for the above.
The website with a payment portal was designed for the symposium conducted by the Society of Civil Engineers for the year 2019. This was the first mainstream project for the Team Enigma having to print it's first invoice.
A website for a startup and maintaining it for the whole year. And Buzzark was just one of them. Nothing more. Nothing less.
We helped Musicmaster get an M.V.P. done within a very short time span. This gave Team Enigma the experience to work under pressure.
We as Team Enigma had to fix the website layouts for different resolutions and also squashed a bug that locked the page from scrolling. That website was for Enantra by the Capitalize Entrepreneurship Club of CEG.
Getting a good response for the last symposium website for the Society of Civil Engineers, we designed the website for the successive symposium as well.


Document System Tutorial Part 9 - Updating the Document Version¶
In this part, we will talk about updating the document version automatically if the file has changed by the user after saving the specific document in the model.
First of all, go to document.go in the models folder. Create a Save function below the model struct. Inside it, create two variables named docChange and newDoc. Set both of them to false.
// Save !
func (d *Document) Save() {
// Initialized variables
docChange := false
newDoc := false
}
Now let’s create a validation that checks whether the document record is new or existing to be done by the user. If the document ID is not 0, the record is existing. Otherwise, the user is creating a new document. Put it below the newDoc variable.
// Checks whether the document record is new or existing
if d.ID != 0 {
// Initializes the Document model
oldDoc := Document{}
// Gets the ID of the old Document
uadmin.Get(&oldDoc, "id = ?", d.ID)
// Checks if the file is changed or updated
if d.File != oldDoc.File {
docChange = true
}
} else {
// New document record
docChange = true
newDoc = true
}
// Save the Document
uadmin.Save(d)
Create a validation that checks if the document is changed, the document version number will automatically get updated.
// Checks whether the document record has changed
if docChange {
// Sets the document value to the DocumentVersion fields
ver := DocumentVersion{}
ver.Date = time.Now()
ver.DocumentID = d.ID
ver.File = d.File
ver.Format = d.Format
// Counts the version number based on the DocumentID and increment it
// by 1
ver.Number = uadmin.Count([]DocumentVersion{}, "document_id = ?", d.ID) + 1
// Save the document version
uadmin.Save(&ver)
}
Below the uadmin.Save(&ver) inside the docChange validation, create another validation that checks if the document is a new record, it will set the values to the DocumentVersion model fields.
// Sets values to the DocumentUser model fields
creator := DocumentUser{
UserID: user.ID,
DocumentID: d.ID,
Read: true,
Edit: true,
Add: true,
Delete: true,
}
// Save the document user
uadmin.Save(&creator)
Now let’s run your application. Go to Document System Dashboard then open DOCUMENTS.
Click the existing record that you have.
Change the file in the document record.
Result
Go back to Document System Dashboard then open DOCUMENT VERSIONS.
The document Computer is now version 2 because you changed the file.
Click here to view our progress so far.
In the next part, we will discuss about creating a group permission for a specific model to the user group with some access levels.


/**
* The status of a Pipeline.
*/
export const pipelineStatus = {
completed: 'completed',
canceled: 'canceled',
rejected: 'rejected',
running: 'running'
};
/**
* A callback to indicate when pipeline processing should advance to the next step
* or be aborted.
*/
interface Next {
/**
* Indicates the successful completion of the pipeline step.
*/
(): Promise<any>,
/**
* Indicates the successful completion of the entire pipeline.
*/
complete: (result: any) => Promise<any>,
/**
* Indicates that the pipeline should cancel processing.
*/
cancel: (result: any) => Promise<any>,
/**
* Indicates that pipeline processing has failed and should be stopped.
*/
reject: (result: any) => Promise<any>
}
/**
* A step to be run during processing of the pipeline.
*/
interface PipelineStep {
/**
* Execute the pipeline step. The step should invoke next(), next.complete(),
* next.cancel(), or next.reject() to allow the pipeline to continue.
*
* @param instruction The navigation instruction.
* @param next The next step in the pipeline.
*/
run(instruction: NavigationInstruction, next: Next): void;
}
/**
* The result of a pipeline run.
*/
interface PipelineResult {
status: string;
instruction: NavigationInstruction;
output: any;
completed: boolean;
}
/**
* The class responsible for managing and processing the navigation pipeline.
*/
export class Pipeline {
/**
* The pipeline steps.
*/
steps: Array<Function|PipelineStep> = [];
/**
* Adds a step to the pipeline.
*
* @param step The pipeline step.
*/
addStep(step: PipelineStep): Pipeline {
let run;
Iif (typeof step === 'function') {
run = step;
} else Iif (typeof step.getSteps === 'function') {
let steps = step.getSteps();
for (let i = 0, l = steps.length; i < l; i++) {
this.addStep(steps[i]);
}
return this;
} else {
run = step.run.bind(step);
}
this.steps.push(run);
return this;
}
/**
* Runs the pipeline.
*
* @param instruction The navigation instruction to process.
*/
run(instruction: NavigationInstruction): Promise<PipelineResult> {
let index = -1;
let steps = this.steps;
function next() {
index++;
Eif (index < steps.length) {
let currentStep = steps[index];
try {
return currentStep(instruction, next);
} catch (e) {
return next.reject(e);
}
} else {
return next.complete();
}
}
next.complete = createCompletionHandler(next, pipelineStatus.completed);
next.cancel = createCompletionHandler(next, pipelineStatus.canceled);
next.reject = createCompletionHandler(next, pipelineStatus.rejected);
return next();
}
}
function createCompletionHandler(next, status) {
return (output) => {
return Promise.resolve({ status, output, completed: status === pipelineStatus.completed });
};
}
|


The Dragon Horn
The king has died mysteriously, and with various claims to the throne, the people decide to hold an election on who should be the next king. As you were the king's younger brother, it's between you, your younger brother, and your niece, along with a few other high lords. You know what you need to rally everyone to your side - the legendary Dragon Horn, which has the power to tame dragons and bring your people to greatness. But when you take the podium, do not blow the horn yourself!  But then, the throne is yours...


Recently, it seems that the only time I’ve spent with my blog has been moving it to other platforms. Hopefully, that will change with the move to Octopress. Since this is mainly a programming / technical blog, I thought it would be great to host the blog with Github pages.
The Octopress install was pretty simple.. I mostly just followed the steps on the Octopress Setup page:
- Install Ruby / rbenv
- Install git
- Clone the Octopress repo
- Export posts from Wordpress
- Convert the Wordpress xml file to Jekyll with wpXml2Jekyll
- rake generate / rake deploy / git commit origin source (from the examples)
The part that gave me the most trouble was hiding legacy posts from the main page area. I wanted to keep some of the posts around for posterity but I didn’t want them to show up with any sort of prominence on the blog. To that end, I created a folder called ‘Legacy’ and a folder within this newly created folder called ‘_posts’. From there, I moved the markdown versions of the posts into this folder. So far so good…
When I fired up the preview for Octopress, the blog was still loading all the legacy posts in the content area. I asked a question on Stack Overflow (that was subsequently shut down for not being programmy enough – whoops) and tried a couple other things until I stumbled upon this post on Hiding Pages from Octopress. Making the suggested changes produced the results I was looking for. Mission Accomplished – for now. I may try to change the ‘Recent Posts’ widget so it does not show legacy posts, however, that won’t be an issue when there is more content.
So, as of this post, I’ve used all Octopress for all of one new article but I really like the workflow; storing the posts in github is definitely an added benefit. Hopefully, I will continue writing beyond this post but I guess we will see.


ADD EMPLOYEE
Employee ID:
First Name:
Last Name:
Address:
Date Of Joining:
Bank Name:
Bank Branch:
Account Number:
Salary:
Email ID:
Phone Number:
Education level:
Job description:
Accountant
Assistant Accountant
Manager


This plugin allows a job to be run after all the immediate downstream jobs have completed. In this way, the execution can branch out and perform many steps in parallel, and then run a final aggregation step just once after all the parallel work is finished.
The plugin is useful for creating a 'diamond' shape project dependency. This means there is a single parent job that starts several downstream jobs. Once those jobs are finished, a single aggregation job runs. More complex interactions are not possible with this plugin.
The downstream projects are specified using Hudson's normal project relationship mechanism.
Plugin Information |
|---|
View Join on the plugin site for more information. |
Example:
Our build consists of four jobs - test, testDown1, testDown2 and testJoin. Basically they have to run in sequence, but testDown1 and testDown2 could be run in parallel.(Maybe something like build - run tests - metrics - release). The logical overview of our jobs would be:
First we have to define our four jobs. With normal Hudson job dependency ("Build other projects") we add testDown1 and testDown2 to test. This is the fork in the diagram.
Adding the testJoin in that way is not possible, because would start it immediately after finishing the test-job. This is where this plugin jumps into: the test-job configures the Join Trigger and specifies the job to run after the join. The plugin is now able to start the testJoin job - but it needs to know when the forked jobs have finished. That's why we add the Join Trigger Upstream Notifier to these jobs.
Now the plugin gets the list of all forked jobs by its base job (test), gets informed by all forked jobs, waits for all "own" jobs to be completed and then starts the final job (testJoin).
Configuration of the base job (test):
Configuration of forked jobs - testDown1 + testDown2:
No configuration required.
Having multiple forks&joins in Hudson
The first example shows the use of this plugin for creating one 'diamond' job dependency. Having multiple diamonds is also easy (I whished it would be in real world ) But the resulting logical overview is a little bit different:
You have two base jobs (testA and testB) and Hudson forks them in the first step. Each of the diamonds has its own fork (2A and 2B). But ALL forked jobs inform the SAME observer (here shown as join bar JobJoin Observer). This ONE observer has two joins configured: the A-diamond and the B-diamond. And each fragment waits for recieving all the notifications it needs.
Example:
- Hudson starts
- Hudson starts testB
- Hudson starts testA
- testA finishes
- Hudson starts downA1
- Hudson starts downA2
- testB finishes
- Hudson starts downB2
- Hudson starts downB1
- downA2 finishes
- downA2 notifies the plugin
- PlugIn: downA2 belongs to diamond A
- PlugIn: diamond A requires downA1 and downA2
- PlugIn: diamond A is missing downA1, so do nothing
- downB2 finishes
- downB2 notifies the plugin
- PlugIn: downB2 belongs to diamond B
- PlugIn: diamond B requires downB1 and downB2
- PlugIn: diamond B is missing downB1, so do nothing
- downB1 finishes
- downB1 notifies the plugin
- PlugIn: downB1 belongs to diamond B
- PlugIn: diamond B requires downB1 and downB2
- PlugIn: all notifications recieved, so start joinB
- downA1 finishes
- downA1 notifies the plugin
- PlugIn: downA1 belongs to diamond A
- PlugIn: diamond A requires downA1 and downA2
- PlugIn: all notifications recieved, so start joinA
Build Parameters
By default, parameters of the current build will not be passed to the join project (like the default build trigger). If you want to do this, choose "Post-Join Action" -> "Trigger parameterized build on other project" and then choose "Current Build Parameters" (or other parameters you want to use).
For example:
Although you can specify in your Post-Join Actions build triggers other than "Stable", only "Stable" seems to work.
Changelog
Version 1.16 - August 2, 2015
- Removed integration with deprecated CopyArchiver Plugin (pull #6)
- Pick up job renames properly
- JENKINS-16201 - Handle cache reloads correctly. Previous behavior may not see all downstream jobs as completed, and so would never start the join job
- JENKINS-25710 - Work with folders
Version 1.15 - May 3, 2012
- Supported hierarchical projects (even more)
Version 1.14 - April 5, 2012
- Supported hierarchical projects
Version 1.13 - September 18, 2011
- Add a method fromSameSplitProject to JoinDependency for use in other plugins - e.g. the Build Pipeline View.
Version 1.12 - August 28, 2011
- Add support for downstream-ext plugin
- Fix 9903: Downstream projects include the "join" project when using the downstream-ext plugin
Version 1.11 - July 11, 2011
- Fix 10301: Jenkins does not start when the parameterized trigger plugin with version 2.10 and the join plugin with version 1.10.1 are installed.
Join Plugin does not work with versions of the Parameterized Trigger Plugin prior to 2.10.
Version 1.10.1 - April 11, 2011
- Fix 8443
- Added autocompletion and form validation to join projects text field
Jobs in the join projects field which don't exist will be pruned on save
Version 1.10 - April 11, 2011
- Failure when publishing artifacts
Version 1.9 - September 13, 2010
- Fix NPE on newer versions of Hudson when adding a post-build action like the copy-archiver or the parameterized-trigger plugin (7344)
- Run parametrized-trigger after join should work again on Hudson version newer than 1.341 (5602)
- Respect disabled projects: Start join projects when all non-disabled downstream projects are finished (5972).
Version 1.7 - January 16, 2010
- Avoid error if parameterized-trigger plugin is installed, but current project doesn't use a parameterized BuildTrigger. (5159)
Version 1.6 - September 30, 2009
- The join plugin will now wait for downstream builds triggered by the parameterized-trigger plugin, in addition to the built-in downstream projects, before performing the join actions.
- Implement the
getRequiredMonitorService
method to indicate no dependency on the previous build. This should allow more parallelism when using concurrent builds.
Version 1.5 - September 18, 2009
- Fix problem where email recipients were cleared on job save (4384)
Version 1.4 - September 2, 2009
- Fix NPE for builds that are automatically upgraded from version 1.2 or earlier (4370)
- Re-add Maven projects as applicable for the Join plugin. Matrix (multi-config) projects remain incompatible. Feedback of using this plugin with Maven projects is sought.
Version 1.3 - August 31, 2009
- Remove console log warnings from builds that are not using the join plugin (report)
- Provide initial support for running arbitrary post-build actions as part of the join process. The parameterized-build plugin is the first candidate (3959)
- Only offer Join plugin with Freestyle builds, due to report of Matrix build incompatibility. (report)
Version 1.2 - June 28, 2009
- Downstream failure detection was broken previous to this version. Previously, the join projects were started no matter what the result of the downstream builds. With this fix, failed downstream builds block the join projects from being started (report)
Version 1.1 - May 30, 2009
- Fix a NPE that occurs when the join plugin is enabled, but no downstream jobs are specified (report)
- Start the join projects immediately if there are no downstream jobs specified.
Version 1.0 - May 23, 2009
- Basic support for joining. After the downstream jobs finish, a comma separated list of jobs can be started as the join jobs.
Attachments:
Picture 6.png (image/png)
join-20090511.hpi (application/octet-stream)
JobJoinPlugin-Config.jpg (image/jpeg)
Two Diamonds.jpg (image/jpeg)
JobJoinPlugin.mdzip (application/octet-stream)
join-20090513.hpi (application/octet-stream)
config.JPG (image/pjpeg)
join-parameterized.png (image/png)


Suspend/Resume
This program will put the platform into suspend mode using the pm_suspend.sh script. The display will blank and the network connection will shutdown. You can wake the system by sending a key press to the serial console or touching the touchscreen.