文章目录
- 一、宏macro的使用方法
- 二、合并重复题录的macro代码
- 2.1 下载并加载macro代码
- 2.2 显示重复题录并合并
- 2.3 合并的规则
- 2.4 其他
- 附:macro代码
一、宏macro的使用方法
参考官方文档 Using macros - Citavi 6 Manual
- Macro files have the .cs file extension. If you received the macro in a ZIP archive, be sure to extract it from the ZIP first.
- Start Citavi and open the project you want to work on.
- Important Back up the project before running a macro on it (File>Create backup > Creating a backup). Creating a backup is very important because the changes made by a macro cannot be undone!
- Many macros apply to the current selection only, so if you want them to apply only to some references, use the filter or search features to create a selection first. (You can identify a macro that applies to the current selection because the macro’s program code will contain a command with the ending “.GetFilteredReferences()”.)
- Click Tools > Macro editor or press Alt+F11 to open the macro ediotr. It can take a few seconds for the macro editor to open.
- In the Macro Editor, on the File menu, click Open and choose the macro file (.cs) you prepared in step 1.
- Click Compile. No errors should appear in the lower pane of the window.
- Click Run to run the macro. You will be asked to confirm that you created a backup. If you haven’t, click Cancel, create the backup, and then continue.
二、合并重复题录的macro代码
2.1 下载并加载macro代码
下载地址: https://github.com/istvank/Citavi-Macros/blob/master/merge-duplicates.cs
打开macro editor操作面板并加载下载的.cs文件
Citavi的宏其实类似于一个临时插件,用一次加载一次。
2.2 显示重复题录并合并
然后选中两个重复的题录(目前的macro代码只支持两个合并),点击 run(在上一步的时候已经点过 Compile),即可合并。
2.3 合并的规则
- 对于Reference面板中的内容,如果遇到不一样的信息,则把两个信息合并,用 // 分开。一个有另一个没有的则取并集。
- 对于划分的category和group也不会丢弃信息,而是取并集。
- 但对于其他部分的内容,比如Evaluation和Abstract,则只保留一个。(应该是保留列表中靠前的一个)
- 附件的话会把附件都放进来,不会删除某个附件。
- 笔记的话内容都会保留,但排序靠后的题录的笔记会损失和pdf文件的超链接。
2.4 其他
重启Citavi后,之前加载的macro代码会清空失效。
附:macro代码
如不方便下载,可自己建立一个 .cs 代码文件。
// Copyright 2018 István Koren
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.using System;
using System.Linq;
using System.ComponentModel;
using System.Collections.Generic;
using System.Windows.Forms;using SwissAcademic.Citavi;
using SwissAcademic.Citavi.Metadata;
using SwissAcademic.Citavi.Shell;
using SwissAcademic.Collections;// Implementation of macro editor is preliminary and experimental.
// The Citavi object model is subject to change in future version.public static class CitaviMacro
{public static void Main(){//if this macro should ALWAYS affect all titles in active project, choose first option//if this macro should affect just filtered rows if there is a filter applied and ALL if not, choose second option//ProjectReferenceCollection references = Program.ActiveProjectShell.Project.References; List<Reference> references = Program.ActiveProjectShell.PrimaryMainForm.GetSelectedReferences();//if we need a ref to the active projectSwissAcademic.Citavi.Project activeProject = Program.ActiveProjectShell.Project;if (references.Count == 2){if (references[0].ReferenceType == references[1].ReferenceType){string originalTitle = references[0].Title;// CreatedOn, check which one is older and then take that CreatedOn and CreatedByif (DateTime.Compare(references[0].CreatedOn, references[1].CreatedOn) > 0){// second reference is older// CreatedOn is write-protected. We therefore switch the references...Reference newer = references[0];references[0] = references[1];references[1] = newer;}// ModifiedOn is write-protected. It will be updated anyways now.// Abstract, naive approach...if (references[0].Abstract.Text.Trim().Length < references[1].Abstract.Text.Trim().Length){references[0].Abstract.Text = references[1].Abstract.Text;}// AccessDate, take newer one//TODO: accessdate would need to be parsed// right now, we just check if there is one, we take it, otherwise we leave it empty.if (references[0].AccessDate.Length < references[1].AccessDate.Length){references[0].AccessDate = references[1].AccessDate;}// Additionsreferences[0].Additions = MergeOrCombine(references[0].Additions, references[1].Additions);// CitationKey, check if CitationKeyUpdateType is 0 at one reference if yes, take that oneif ((references[0].CitationKeyUpdateType == UpdateType.Automatic) && (references[1].CitationKeyUpdateType == UpdateType.Manual)){references[0].CitationKey = references[1].CitationKey;references[0].CitationKeyUpdateType = references[1].CitationKeyUpdateType;}// CoverPathif (references[0].CoverPath.LinkedResourceType == LinkedResourceType.Empty){references[0].CoverPath = references[1].CoverPath;}// CustomFields (1-9)references[0].CustomField1 = MergeOrCombine(references[0].CustomField1, references[1].CustomField1);references[0].CustomField2 = MergeOrCombine(references[0].CustomField2, references[1].CustomField2);references[0].CustomField3 = MergeOrCombine(references[0].CustomField3, references[1].CustomField3);references[0].CustomField4 = MergeOrCombine(references[0].CustomField4, references[1].CustomField4);references[0].CustomField5 = MergeOrCombine(references[0].CustomField5, references[1].CustomField5);references[0].CustomField6 = MergeOrCombine(references[0].CustomField6, references[1].CustomField6);references[0].CustomField7 = MergeOrCombine(references[0].CustomField7, references[1].CustomField7);references[0].CustomField8 = MergeOrCombine(references[0].CustomField8, references[1].CustomField8);references[0].CustomField9 = MergeOrCombine(references[0].CustomField9, references[1].CustomField9);// Date (string typereferences[0].Date = MergeOrCombine(references[0].Date, references[1].Date);// Date2 (string type)references[0].Date2 = MergeOrCombine(references[0].Date2, references[1].Date2);// DOIreferences[0].Doi = MergeOrCombine(references[0].Doi, references[1].Doi);// Editionreferences[0].Edition = MergeOrCombine(references[0].Edition, references[1].Edition);// EndPageif (references[0].PageRange.ToString() == ""){references[0].PageRange = references[1].PageRange;}// Evaluation, naive approach...if (references[0].Evaluation.Text.Trim().Length < references[1].Evaluation.Text.Trim().Length){references[0].Evaluation.Text = references[1].Evaluation.Text;}// HasLabel1 and HasLabel2if (references[1].HasLabel1){references[0].HasLabel1 = references[1].HasLabel1;}if (references[1].HasLabel2){references[0].HasLabel2 = references[1].HasLabel2;}// ISBNreferences[0].Isbn = MergeOrCombine(references[0].Isbn.ToString(), references[1].Isbn.ToString());// Languagereferences[0].Language = MergeOrCombine(references[0].Language, references[1].Language);// Notesreferences[0].Notes = MergeOrCombine(references[0].Notes, references[1].Notes);// Numberreferences[0].Number = MergeOrCombine(references[0].Number, references[1].Number);// NumberOfVolumesreferences[0].NumberOfVolumes = MergeOrCombine(references[0].NumberOfVolumes, references[1].NumberOfVolumes);// OnlineAddressreferences[0].OnlineAddress = MergeOrCombine(references[0].OnlineAddress, references[1].OnlineAddress);// OriginalCheckedByreferences[0].OriginalCheckedBy = MergeOrCombine(references[0].OriginalCheckedBy, references[1].OriginalCheckedBy);// OriginalPublicationreferences[0].OriginalPublication = MergeOrCombine(references[0].OriginalPublication, references[1].OriginalPublication);// PageCount (text)//TODO: apparently it is a calculated field// PageCountNumeralSystem (int=0)//TODO: apparently it is a calculated field// PageRangeNumberingType (int=0)//TODO: apparently it is a calculated field// PageRangeNumeralSystem (int=0)//TODO: apparently it is a calculated field// ParallelTitlereferences[0].ParallelTitle = MergeOrCombine(references[0].ParallelTitle, references[1].ParallelTitle);// PeriodicalID, naive approach...if ((references[0].Periodical == null) || (((references[0].Periodical != null) && (references[1].Periodical != null)) && (references[0].Periodical.ToString().Length < references[1].Periodical.ToString().Length))){references[0].Periodical = references[1].Periodical;}// PlaceOfPublicationreferences[0].PlaceOfPublication = MergeOrCombine(references[0].PlaceOfPublication, references[1].PlaceOfPublication);// Pricereferences[0].Price = MergeOrCombine(references[0].Price, references[1].Price);// PubMedIDreferences[0].PubMedId = MergeOrCombine(references[0].PubMedId, references[1].PubMedId);// Rating (take average)references[0].Rating = (short) Math.Floor((decimal) ((references[0].Rating + references[1].Rating) / 2));// (!) ReferenceType (not supported)// SequenceNumber (take the one of first, as second reference will be deleted)// ShortTitle, check if ShortTitleUpdateType is 0 at one reference if yes, take that oneif ((references[0].ShortTitleUpdateType == UpdateType.Automatic) && (references[1].ShortTitleUpdateType == UpdateType.Manual)){references[0].ShortTitle = references[1].ShortTitle;}else if ((references[0].ShortTitleUpdateType == UpdateType.Manual) && (references[1].ShortTitleUpdateType == UpdateType.Manual)){references[0].ShortTitle = MergeOrCombine(references[0].ShortTitle, references[1].ShortTitle);}// SourceOfBibliographicInformationreferences[0].SourceOfBibliographicInformation = MergeOrCombine(references[0].SourceOfBibliographicInformation, references[1].SourceOfBibliographicInformation);// SpecificFields (1-7)references[0].SpecificField1 = MergeOrCombine(references[0].SpecificField1, references[1].SpecificField1);references[0].SpecificField2 = MergeOrCombine(references[0].SpecificField2, references[1].SpecificField2);references[0].SpecificField3 = MergeOrCombine(references[0].SpecificField3, references[1].SpecificField3);references[0].SpecificField4 = MergeOrCombine(references[0].SpecificField4, references[1].SpecificField4);references[0].SpecificField5 = MergeOrCombine(references[0].SpecificField5, references[1].SpecificField5);references[0].SpecificField6 = MergeOrCombine(references[0].SpecificField6, references[1].SpecificField6);references[0].SpecificField7 = MergeOrCombine(references[0].SpecificField7, references[1].SpecificField7);// StartPage//TODO: see page range// StorageMediumreferences[0].StorageMedium = MergeOrCombine(references[0].StorageMedium, references[1].StorageMedium);// Subtitlereferences[0].Subtitle = MergeOrCombine(references[0].Subtitle, references[1].Subtitle);// SubtitleTagged//TODO: we are not merging SubtitleTagged as that changes the Subtitle as well//references[0].SubtitleTagged = MergeOrCombine(references[0].SubtitleTagged, references[1].SubtitleTagged);// TableOfContents, naive approach... if ((references[0].TableOfContents == null) || (((references[0].TableOfContents != null) && (references[1].TableOfContents != null)) && (references[0].TableOfContents.ToString().Length < references[1].TableOfContents.ToString().Length))){references[0].TableOfContents.Text = references[1].TableOfContents.Text;}// TextLinksreferences[0].TextLinks = MergeOrCombine(references[0].TextLinks, references[1].TextLinks);// Titlereferences[0].Title = MergeOrCombine(references[0].Title, references[1].Title);// TitleTagged//TODO: we are not merging TitleTagged as that changes the Title as well//references[0].TitleTagged = MergeOrCombine(references[0].TitleTagged, references[1].TitleTagged);// TitleInOtherLanguagesreferences[0].TitleInOtherLanguages = MergeOrCombine(references[0].TitleInOtherLanguages, references[1].TitleInOtherLanguages);// TitleSupplementreferences[0].TitleSupplement = MergeOrCombine(references[0].TitleSupplement, references[1].TitleSupplement);// TitleSupplementTagged//TODO: we are not merging TitleSupplementTagged as that changes the TitleSupplement as well//references[0].TitleSupplementTagged = MergeOrCombine(references[0].TitleSupplementTagged, references[1].TitleSupplementTagged);// TranslatedTitlereferences[0].TranslatedTitle = MergeOrCombine(references[0].TranslatedTitle, references[1].TranslatedTitle);// UniformTitlereferences[0].UniformTitle = MergeOrCombine(references[0].UniformTitle, references[1].UniformTitle);// Volumereferences[0].Volume = MergeOrCombine(references[0].Volume, references[1].Volume);// Yearreferences[0].Year = MergeOrCombine(references[0].Year, references[1].Year);// ReservedData//TODO: apparently cannot be set// RecordVersion (?)//TODO: apparently cannot be set// FOREIGN KEY fields// Locationsforeach(Location location in references[1].Locations){if (!references[0].Locations.Contains(location)){references[0].Locations.Add(location);}}// Groupsreferences[0].Groups.AddRange(references[1].Groups);// Quotationsreferences[0].Quotations.AddRange(references[1].Quotations);// ReferenceAuthors//references[0].Authors.AddRange(references[1].Authors);foreach (Person author in references[1].Authors){if (!references[0].Authors.Contains(author)){references[0].Authors.Add(author);}}// ReferenceCategoryreferences[0].Categories.AddRange(references[1].Categories);// ReferenceCollaboratorforeach (Person collaborator in references[1].Collaborators){if (!references[0].Collaborators.Contains(collaborator)){references[0].Collaborators.Add(collaborator);}}// ReferenceEditorforeach (Person editor in references[1].Editors){if (!references[0].Editors.Contains(editor)){references[0].Editors.Add(editor);}}// ReferenceKeywordforeach (Keyword keyword in references[1].Keywords){if (!references[0].Keywords.Contains(keyword)){references[0].Keywords.Add(keyword);}}// ReferenceOrganizationforeach (Person organization in references[1].Organizations){if (!references[0].Organizations.Contains(organization)){references[0].Organizations.Add(organization);}}// ReferenceOthersInvolvedforeach (Person otherinvolved in references[1].OthersInvolved){if (!references[0].OthersInvolved.Contains(otherinvolved)){references[0].OthersInvolved.Add(otherinvolved);}}// ReferencePublisherforeach (Publisher publisher in references[1].Publishers){if (!references[0].Publishers.Contains(publisher)){references[0].Publishers.Add(publisher);}}// ReferenceReference// adding ChildReferences does not work//references[0].ChildReferences.AddRange(references[1].ChildReferences);Reference[] childReferences = references[1].ChildReferences.ToArray();foreach (Reference child in childReferences){child.ParentReference = references[0];}// SeriesTitle, naive approachif ((references[0].SeriesTitle == null) || (((references[0].SeriesTitle != null) && (references[1].SeriesTitle != null)) && (references[0].SeriesTitle.ToString().Length < references[1].SeriesTitle.ToString().Length))){references[0].SeriesTitle = references[1].SeriesTitle;}// change crossreferencesforeach (EntityLink entityLink in references[1].EntityLinks){if (entityLink.Source == references[1]){entityLink.Source = references[0];}else if (entityLink.Target == references[1]){entityLink.Target = references[0];}}// write Note that the reference has been mergedif (references[0].Notes.Trim().Length > 0){references[0].Notes += " |";}references[0].Notes += " This reference has been merged with a duplicate by CitaviBot.";// DONE! remove second referenceactiveProject.References.Remove(references[1]);}else{MessageBox.Show("Currently this script only supports merging two references of the same type. Please convert and try again.");}}else{MessageBox.Show("Currently this script only supports merging two references. Please select two and try again.");}}private static string MergeOrCombine(string first, string second) {first = first.Trim();second = second.Trim();// do not compare ignore case, otherwise we might lose capitalization information; in that case we rely on manual edits after the mergeif (String.Compare(first, second, false) == 0){// easy case, they are the same!return first;}else if (first.Length == 0){return second;}else if (second.Length == 0){return first;}else{return first + " // " + second;}}
}