dp2 icon indicating copy to clipboard operation
dp2 copied to clipboard

为某用户整理数据

Open DigitalPlatform opened this issue 3 years ago • 3 comments

数据早先是从 dt1000 升级过来的。经检查发现有部分数据未能导入 dp2 系统,需要从 dt1000 导出的 ISO2709 文件重新转换为 .bdf 文件,然后增补到 dp2 系统中。

导入 .bdf 文件

按住 Ctrl 键打开“从书目转储文件导入”窗口。

“转换”属性页,输入一个适当的“册记录批次号”。

“目标库”属性页,目标书目库名输入“中文图书”。 所有 checkbox 都是 clear 状态。 恢复模式选择“书目+下级记录”。(这是为了避免 dp2library 检查册条码号形态)

DigitalPlatform avatar Jun 04 '22 14:06 DigitalPlatform

从 dt1000 的 ISO2709 创建 .bdf 文件

在 ISO2709 统计窗中创建一个统计方案,名为“xxx dt1000书目转换为bdf”。

这个统计方案在开始运行的时候,会从全部实体库检索出册条码号,然后去掉带有后缀的,剩下的加入一个 Hashtable,用来在处理的时候进行查重,凡是重复的册就不会用于创建 .bdf 文件。

一个 .iso 文件处理期间的册条码号也会进入 Hashtable 查重。

main.cs

using System;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;
using System.Collections;
using System.Collections.Generic;
using System.Web;

using dp2Circulation;
using dp2Circulation.ISO2709Statis;

using DigitalPlatform;
using DigitalPlatform.IO;
using DigitalPlatform.Marc;
using DigitalPlatform.Text;
using DigitalPlatform.Xml;
using DigitalPlatform.LibraryClient;

public class MyStatis : ConvertDt1000ToBdf
{
    MyForm _form = null;

    Hashtable _table = null;

    public override void OnRecord(object sender, StatisEventArgs e)
    {
        string strError = "";
        int nRet = 0;

        _form = (MyForm)sender;

        if (_table == null)
        {
            string fileName = Path.Combine(this.MainForm.UserDir, "temp\\xianyang_barcodes.txt");
            if (File.Exists(fileName) == false)
            {
                nRet = BuildBarcodeFile(fileName,
    out strError);
                if (nRet == -1)
                    goto ERROR1;
            }

            _table = new Hashtable();
            LoadAllItems(
                fileName,
                _table);
        }

        MarcRecord record = new MarcRecord(this.MARC);

        nRet = PrepareSource(
            record,
            out string path,
            out string timestamp,
            out strError);
        if (nRet == -1)
            goto ERROR1;

        string strXml = "";
        nRet = MarcUtil.Marc2XmlEx(this.MARC,
this.Syntax,
ref strXml,
out strError);
        if (nRet == -1)
            goto ERROR1;

        XmlDocument biblio_dom = new XmlDocument();
        biblio_dom.LoadXml(strXml);

        {
            // 准备实体记录
            {
                // return:
                //      -1  出错
                //      0   成功
                //      1   所有册都发生了重复,需要忽略本 MARC 记录
                nRet = BuildEntities(
false,   // _bSeries,
this.MARC,
this.Syntax,
out List<string> entity_xmls,
// out int nThisEntityCount,
out string strWarning,
out strError);
                if (nRet == -1)
                    goto ERROR1;

                if (nRet == 1)
                    return;

                if (entity_xmls.Count == 0)
                    return;

                // 写入 dprms:record 元素
                Writer.WriteStartElement("dprms", "record", DpNs.dprms);

                // 写入书目记录
                {
                    // 写入 dprms:biblio 元素
                    Writer.WriteStartElement("dprms", "biblio", DpNs.dprms);

                    Writer.WriteAttributeString("path", path);
                    Writer.WriteAttributeString("timestamp", timestamp);

                    biblio_dom.DocumentElement.WriteTo(Writer);
                    Writer.WriteEndElement();
                }

                if (String.IsNullOrEmpty(strWarning) == false)
                {
                    string strText = "";
                    string[] lines = strWarning.Split(new char[] { ';', '\r' });
                    for (int i = 0; i < lines.Length; i++)
                    {
                        strText += HttpUtility.HtmlEncode(lines[i]) + "<br/>";
                    }
                    this.WriteToConsole(
                        "记录 " + path + " 的册信息处理过程发出警告: <br/>" + strText);
                }

                if (entity_xmls.Count > 0)
                {
                    Writer.WriteStartElement("dprms", "itemCollection", DpNs.dprms);
                    foreach (var xml in entity_xmls)
                    {
                        XmlDocument item_dom = new XmlDocument();
                        item_dom.LoadXml(xml);

                        Writer.WriteStartElement("dprms", "item", DpNs.dprms);
                        // writer.WriteAttributeString("path", info.OldRecPath);
                        // writer.WriteAttributeString("timestamp", ByteArray.GetHexTimeStampString(info.OldTimestamp));
                        DomUtil.RemoveEmptyElements(item_dom.DocumentElement);
                        item_dom.DocumentElement.WriteContentTo(Writer);
                        Writer.WriteEndElement();
                    }
                    Writer.WriteEndElement();
                }
            }

            Writer.WriteEndElement();
        }

        return;
    ERROR1:
        e.Continue = ContinueType.Error;
        e.ParamString = strError;
    }

    // 将一条MARC记录中包含的实体信息变成XML格式并上传
    // parameters:
    //      bSeries 是否为期刊库? 如果为期刊库,则需要把907和986合并;否则要把906和986合并
    //      strEntityDbName 实体数据库名
    //      strParentRecordID   父记录ID
    //      strMARC 父记录MARC
    // return:
    //      -1  出错
    //      0   成功
    //      1   所有册都发生了重复,需要忽略本 MARC 记录
    int BuildEntities(
        bool bSeries,
        string strMARC,
        string strMarcSyntax,
        out List<string> entity_xmls,
        out string strWarning,
        out string strError)
    {
        strError = "";
        strWarning = "";
        // nThisEntityCount = 0;
        entity_xmls = new List<string>();

        int nRet = 0;

        string strField906or907 = "";
        string strField986 = "";
        string strNextFieldName = "";

        string strWarningParam = "";

        /*
        // 规范化parent id,去掉前面的'0'
        strParentRecordID = strParentRecordID.TrimStart(new char[] { '0' });
        if (String.IsNullOrEmpty(strParentRecordID) == true)
            strParentRecordID = "0";
        */

        // 获得010$a
        string strBiblioPrice = "";
        if (strMarcSyntax == "unimarc")
        {
            strBiblioPrice = MarcUtil.GetFirstSubfield(strMARC,
                "010",
                "d");
        }
        else if (strMarcSyntax == "usmarc")
        {
            strBiblioPrice = MarcUtil.GetFirstSubfield(strMARC,
                "020",
                "c");
        }
        else
        {
            strError = "未知的strMarcSyntax值 '" + strMarcSyntax + "'";
            return -1;
        }

        if (String.IsNullOrEmpty(strBiblioPrice) == false)
        {
            // 正规化价格字符串
            strBiblioPrice = CanonicalizePrice(strBiblioPrice, false);
        }

        // 获得906/907字段
        nRet = MarcUtil.GetField(strMARC,
            bSeries == true ? "907" : "906",
            0,
            out strField906or907,
            out strNextFieldName);
        if (nRet == -1)
        {
            if (bSeries == true)
                strError = "从MARC记录中获得907字段时出错";
            else
                strError = "从MARC记录中获得906字段时出错";
            return -1;
        }
        if (nRet == 0)
            strField906or907 = "";

        // 获得986字段

        // 从记录中得到一个字段
        // parameters:
        //		strMARC		机内格式MARC记录
        //		strFieldName	字段名。如果本参数==null,表示想获取任意字段中的第nIndex个
        //		nIndex		同名字段中的第几个。从0开始计算(任意字段中,序号0个则表示头标区)
        //		strField	[out]输出字段。包括字段名、必要的字段指示符、字段内容。不包含字段结束符。
        //					注意头标区当作一个字段返回,此时strField中不包含字段名,其中一开始就是头标区内容
        //		strNextFieldName	[out]顺便返回所找到的字段其下一个字段的名字
        // return:
        //		-1	出错
        //		0	所指定的字段没有找到
        //		1	找到。找到的字段返回在strField参数中
        nRet = MarcUtil.GetField(strMARC,
            "986",
            0,
            out strField986,
            out strNextFieldName);
        if (nRet == -1)
        {
            strError = "从MARC记录中获得986字段时出错";
            return -1;
        }

        if (nRet == 0)
        {
            // return 0;   // 没有找到986字段
            strField986 = "";
        }
        else
        {
            // 修正986字段内容
            if (strField986.Length <= 5 + 2)
                strField986 = "";
            else
            {
                string strPart = strField986.Substring(5, 2);

                string strDollarA = new string(MarcUtil.SUBFLD, 1) + "a";

                if (strPart != strDollarA)
                {
                    strField986 = strField986.Insert(5, strDollarA);
                }
            }
        }

        /*
        List<ItemGroup> groups = null;

        // 合并906和986字段内容
        nRet = MergeField906and986(strField906or907,
            strField986,
            out groups,
            out strWarningParam,
            out strError);

        if (nRet == -1)
            return -1;

        if (String.IsNullOrEmpty(strWarningParam) == false)
            strWarning += strWarningParam + "; ";
        */
        List<string> results = new List<string>();
        {
            List<string> numbers = new List<string>();
            MarcRecord record = new MarcRecord(strMARC);
            var subfields = record.select("field[@name='906']/subfield[@name='h']");
            foreach (MarcSubfield subfield in subfields)
            {
                string number = CorrectNumber(subfield.Content);
                if (string.IsNullOrEmpty(number) == false)
                    numbers.Add(number);
            }
            subfields = record.select("field[@name='986']/subfield[@name='a']");
            foreach (MarcSubfield subfield in subfields)
            {
                string number = CorrectNumber(subfield.Content);
                if (string.IsNullOrEmpty(number) == false)
                    numbers.Add(number);
            }

            StringUtil.RemoveDupNoSort(ref numbers);

            // 查重
            int dup_count = 0;
            foreach (string number in numbers)
            {
                /*
                // return:
                //      -1  出错
                //      0   没有发现重复
                //      1   发现重复
                nRet = SearchItemDup(
                    number,
                    out strError);
                if (nRet == -1)
                {
                    strError = $"针对 '{number}' 查重时出错: {strError}";
                    return -1;
                }
                if (nRet == 1)
                    dup_count++;
                */
                if (_table.ContainsKey(number))
                    dup_count++;
                else
                {
                    results.Add(number);
                    _table[number] = 1; // 记住,避免同一文件后面的册再重复进入 .bdf 文件
                }
            }

            if (results.Count == 0)
            {
                return 1;
            }
        }

        List<ItemGroup> groups = new List<ItemGroup>();
        foreach (string number in results)
        {
            ItemGroup group = new ItemGroup();
            group.strBarcode = number;
            group.strRegisterNo = "";
            group.strValue = $"$a{number}".Replace("$", new string((char)31, 1));
            groups.Add(group);
        }

        // List<EntityInfo> entityArray = new List<EntityInfo>();

        // 进行子字段组循环
        for (int g = 0; g < groups.Count; g++)
        {
            ItemGroup group = groups[g];

            string strGroup = group.strValue;

            // 处理一个item

            string strXml = "";

            // 构造实体XML记录
            // parameters:
            //      strParentID 父记录ID
            //      strGroup    待转换的图书种记录的986字段中某子字段组片断
            //      strXml      输出的实体XML记录
            // return:
            //      -1  出错
            //      0   成功
            nRet = BuildEntityXmlRecord(
                bSeries,
                // strParentRecordID,
                strGroup,
                strMARC,
                strBiblioPrice,
                group.strMergeComment,
                out strXml,
                out strWarningParam,
                out strError);
            if (nRet == -1)
            {
                strError = "创建实体(序号) " + Convert.ToString(g + 1) + "时发生错误: " + strError;
                return -1;
            }

            if (String.IsNullOrEmpty(strWarningParam) == false)
                strWarning += strWarningParam + "; ";

            // 搜集<location>值
            // 2008/8/22
            XmlDocument entity_dom = new XmlDocument();
            try
            {
                entity_dom.LoadXml(strXml);
            }
            catch (Exception ex)
            {
                strError = "创建实体(序号) " + Convert.ToString(g + 1) + "时发生错误: "
                    + "entity xml装入XMLDOM时发生错误: " + ex.Message;
                return -1;
            }

            string strLocation = DomUtil.GetElementText(entity_dom.DocumentElement, "location");

            // 允许馆藏地点值为空
            if (String.IsNullOrEmpty(strLocation) == true)
                strLocation = "";

            FillValueTable(this.m_locations,
                strLocation);

            entity_xmls.Add(strXml);
        }

        return 0;
    }

    // 修正号码错误
    static string CorrectNumber(string number)
    {
        if (string.IsNullOrEmpty(number))
            return number;
        if (number[0] == 'h' || number[0] == 'H')
            return number.Substring(1);
        return number;
    }

    // 合并906和986字段内容
    static int MergeField906and986(string strField906,
        string strField986,
        // int nEntityBarcodeLength,
        out List<ItemGroup> groups,
        out string strWarning,
        out string strError)
    {
        strError = "";
        groups = null;
        strError = "";
        strWarning = "";

        int nRet = 0;

        List<ItemGroup> groups_906 = null;
        List<ItemGroup> groups_986 = null;

        string strWarningParam = "";

        nRet = BuildGroups(strField906,
            // nEntityBarcodeLength,
            out groups_906,
            out strWarningParam,
            out strError);
        if (nRet == -1)
        {
            strError = "在将906字段分析创建groups对象过程中发生错误: " + strError;
            return -1;
        }

        if (String.IsNullOrEmpty(strWarningParam) == false)
            strWarning += "906字段 " + strWarningParam + "; ";

        nRet = BuildGroups(strField986,
            // nEntityBarcodeLength,
            out groups_986,
            out strWarningParam,
            out strError);
        if (nRet == -1)
        {
            strError = "在将986字段分析创建groups对象过程中发生错误: " + strError;
            return -1;
        }

        if (String.IsNullOrEmpty(strWarningParam) == false)
            strWarning += "986字段 " + strWarningParam + "; ";


        List<ItemGroup> new_groups = new List<ItemGroup>(); // 新增部分

        for (int i = 0; i < groups_906.Count; i++)
        {
            ItemGroup group906 = groups_906[i];

            bool bFound = false;
            for (int j = 0; j < groups_986.Count; j++)
            {
                ItemGroup group986 = groups_986[j];

                if (group906.strBarcode != "")
                {
                    if (group906.strBarcode == group986.strBarcode)
                    {
                        bFound = true;

                        // 重复的情况下,补充986所缺的少量子字段
                        group986.MergeValue(group906,
                            "b");

                        break;
                    }
                }
                else if (group906.strRegisterNo != "")
                {
                    if (group906.strRegisterNo == group986.strRegisterNo)
                    {
                        bFound = true;

                        // 重复的情况下,补充986所缺的少量子字段
                        group986.MergeValue(group906,
                            "b");

                        break;
                    }
                }
            }

            if (bFound == true)
            {
                continue;
            }

            group906.strMergeComment = "从906字段中增补过来";
            new_groups.Add(group906);
        }

        groups = new List<ItemGroup>(); // 结果数组
        groups.AddRange(groups_986);    // 先加入986内的所有事项

        if (new_groups.Count > 0)
            groups.AddRange(new_groups);    // 然后加入新增事项

        return 0;
    }

    // 构造实体 XML 记录
    // parameters:
    //      strParentID 父记录ID
    //      strGroup    待转换的图书种记录的986字段中某子字段组片断
    //      strXml      输出的实体XML记录
    //      strBiblioPrice  种价格。当缺乏册例外价格的时候,自动加入种价格
    // return:
    //      -1  出错
    //      0   成功
    static int BuildEntityXmlRecord(
        bool bSeries,
        string strGroup,
        string strMARC,
        string strBiblioPrice,
        string strMergeComment,
        out string strXml,
        out string strWarning,
        out string strError)
    {
        strXml = "";
        strWarning = "";
        strError = "";

        // 
        XmlDocument dom = new XmlDocument();
        dom.LoadXml("<root />");

        /*
        // 父记录id
        DomUtil.SetElementText(dom.DocumentElement, "parent", strParentID);
        */

        // 册条码

        string strSubfield = "";
        string strNextSubfieldName = "";
        // 从字段或子字段组中得到一个子字段
        // parameters:
        //		strText		字段内容,或者子字段组内容。
        //		textType	表示strText中包含的是字段内容还是组内容。若为ItemType.Field,表示strText参数中为字段;若为ItemType.Group,表示strText参数中为子字段组。
        //		strSubfieldName	子字段名,内容为1位字符。如果==null,表示任意子字段
        //					形式为'a'这样的。
        //		nIndex			想要获得同名子字段中的第几个。从0开始计算。
        //		strSubfield		[out]输出子字段。子字段名(1字符)、子字段内容。
        //		strNextSubfieldName	[out]下一个子字段的名字,内容一个字符
        // return:
        //		-1	出错
        //		0	所指定的子字段没有找到
        //		1	找到。找到的子字段返回在strSubfield参数中
        int nRet = MarcUtil.GetSubfield(strGroup,
            ItemType.Group,
            "a",
            0,
            out strSubfield,
            out strNextSubfieldName);
        if (strSubfield.Length >= 1)
        {
            string strBarcode = strSubfield.Substring(1);

            strBarcode = strBarcode.ToUpper();  // 2008/10/24 new add

            if (string.IsNullOrEmpty(strBarcode) == false)
                DomUtil.SetElementText(dom.DocumentElement, "barcode", strBarcode);
        }

        // 登录号
        nRet = MarcUtil.GetSubfield(strGroup,
ItemType.Group,
"h",
0,
out strSubfield,
out strNextSubfieldName);
        if (strSubfield.Length >= 1)
        {
            string strRegisterNo = strSubfield.Substring(1);

            if (String.IsNullOrEmpty(strRegisterNo) == false)
            {
                DomUtil.SetElementText(dom.DocumentElement, "registerNo", strRegisterNo);
            }
        }

        // 状态?
        DomUtil.SetElementText(dom.DocumentElement, "state", "");

        // 馆藏地点
        nRet = MarcUtil.GetSubfield(strGroup,
            ItemType.Group,
            "b",
            0,
            out strSubfield,
            out strNextSubfieldName);
        if (strSubfield.Length >= 1)
        {
            string strLocation = strSubfield.Substring(1);

            DomUtil.SetElementText(dom.DocumentElement, "location", strLocation);
        }
        else
        {
            // 2022/6/4 默认值
            DomUtil.SetElementText(dom.DocumentElement,
                "location",
                "xxx/图书馆");
        }


        // 价格
        // 先找子字段组中的$d 找不到才找982$b

        nRet = MarcUtil.GetSubfield(strGroup,
ItemType.Group,
"d",
0,
out strSubfield,
out strNextSubfieldName);
        string strPrice = "";

        if (strSubfield.Length >= 1)
        {
            strPrice = strSubfield.Substring(1);
            // 正规化价格字符串
            strPrice = CanonicalizePrice(strPrice, false);
        }
        else
        {
            strPrice = strBiblioPrice;
        }

        // 如果从$d中获得的价格内容为空,则从982$b中获得
        if (String.IsNullOrEmpty(strPrice) == true)
        {
            // 以字段/子字段名从记录中得到第一个子字段内容。
            // parameters:
            //		strMARC	机内格式MARC记录
            //		strFieldName	字段名。内容为字符
            //		strSubfieldName	子字段名。内容为1字符
            // return:
            //		""	空字符串。表示没有找到指定的字段或子字段。
            //		其他	子字段内容。注意,这是子字段纯内容,其中并不包含子字段名。
            strPrice = MarcUtil.GetFirstSubfield(strMARC,
                "982",
                "b");
        }

        if (string.IsNullOrEmpty(strPrice) == false)
            DomUtil.SetElementText(dom.DocumentElement, "price", strPrice);

        // 图书册类型
        // 先找这里的$f 如果没有,再找982$a?
        nRet = MarcUtil.GetSubfield(strGroup,
ItemType.Group,
"f",
0,
out strSubfield,
out strNextSubfieldName);
        string strBookType = "";
        if (strSubfield.Length >= 1)
        {
            strBookType = strSubfield.Substring(1);
        }

        // 如果从$f中获得的册类型为空,则从982$a中获得
        if (String.IsNullOrEmpty(strBookType) == true)
        {
            // 以字段/子字段名从记录中得到第一个子字段内容。
            // parameters:
            //		strMARC	机内格式MARC记录
            //		strFieldName	字段名。内容为字符
            //		strSubfieldName	子字段名。内容为1字符
            // return:
            //		""	空字符串。表示没有找到指定的字段或子字段。
            //		其他	子字段内容。注意,这是子字段纯内容,其中并不包含子字段名。
            strBookType = MarcUtil.GetFirstSubfield(strMARC,
                "982",
                "a");
        }

        // 2022/6/4 默认值
        if (string.IsNullOrEmpty(strBookType))
            strBookType = "普通";

        if (string.IsNullOrEmpty(strBookType) == false)
            DomUtil.SetElementText(dom.DocumentElement, "bookType", strBookType);

        // 注释
        nRet = MarcUtil.GetSubfield(strGroup,
ItemType.Group,
"z",
0,
out strSubfield,
out strNextSubfieldName);
        string strComment = "";
        if (strSubfield.Length >= 1)
        {
            strComment = strSubfield.Substring(1);
        }

        if (String.IsNullOrEmpty(strComment) == false)
        {
            DomUtil.SetElementText(dom.DocumentElement, "comment", strComment);
        }

        // 借阅者
        nRet = MarcUtil.GetSubfield(strGroup,
ItemType.Group,
"r",
0,
out strSubfield,
out strNextSubfieldName);
        string strBorrower = "";
        if (strSubfield.Length >= 1)
        {
            strBorrower = strSubfield.Substring(1);
        }

        if (String.IsNullOrEmpty(strBorrower) == false)
        {
            strBorrower = strBorrower.ToUpper();  // 2008/10/24 new add

            DomUtil.SetElementText(dom.DocumentElement, "borrower", strBorrower);

            // 借阅日期
            nRet = MarcUtil.GetSubfield(strGroup,
ItemType.Group,
"t",
0,
out strSubfield,
out strNextSubfieldName);
            string strBorrowDate = "";
            if (strSubfield.Length >= 1)
            {
                strBorrowDate = strSubfield.Substring(1);

                // 格式为 20060625, 需要转换为rfc
                if (strBorrowDate.Length == 8)
                {
                    /*
                    IFormatProvider culture = new CultureInfo("zh-CN", true);

                    DateTime time;
                    try
                    {
                        time = DateTime.ParseExact(strBorrowDate, "yyyyMMdd", culture);
                    }
                    catch
                    {
                        strError = "子字段组中$t内容中的借阅日期 '" + strBorrowDate + "' 字符串转换为DateTime对象时出错";
                        return -1;
                    }

                    time = time.ToUniversalTime();
                    strBorrowDate = DateTimeUtil.Rfc1123DateTimeString(time);
                     * */

                    string strTarget = "";

                    nRet = DateTimeUtil.Date8toRfc1123(strBorrowDate,
                    out strTarget,
                    out strError);
                    if (nRet == -1)
                    {
                        strWarning += "子字段组中$t内容中的借阅日期 '" + strBorrowDate + "' 格式出错: " + strError;
                        strBorrowDate = "";
                    }
                    else
                    {
                        strBorrowDate = strTarget;
                    }

                }
                else if (String.IsNullOrEmpty(strBorrowDate) == false)
                {
                    strWarning += "$t中日期值 '" + strBorrowDate + "' 格式错误,长度应为8字符 ";
                    strBorrowDate = "";
                }
            }

            if (String.IsNullOrEmpty(strBorrowDate) == false)
            {
                DomUtil.SetElementText(dom.DocumentElement, "borrowDate", strBorrowDate);
            }

            // 借阅期限
            if (String.IsNullOrEmpty(strBorrowDate) == false)
            {
                DomUtil.SetElementText(dom.DocumentElement, "borrowPeriod", "1day"); // 象征性地为1天。因为<borrowDate>中的值实际为应还日期
            }
        }

        {
            // 2009/9/17 new add
            // 普通图书

            // $v
            nRet = MarcUtil.GetSubfield(strGroup,
                ItemType.Group,
                "v",
                0,
                out strSubfield,
                out strNextSubfieldName);
            string strVolumeRange = "";
            if (strSubfield.Length >= 1)
            {
                strVolumeRange = strSubfield.Substring(1);
            }

            if (String.IsNullOrEmpty(strVolumeRange) == false)
            {
                DomUtil.SetElementText(dom.DocumentElement, "volume", strVolumeRange);
            }
        }

        // 状态
        nRet = MarcUtil.GetSubfield(strGroup,
            ItemType.Group,
            "s",
            0,
            out strSubfield,
            out strNextSubfieldName);
        string strState = "";
        if (strSubfield.Length >= 1)
        {
            strState = strSubfield.Substring(1);
        }

        if (String.IsNullOrEmpty(strState) == false)
        {
            DomUtil.SetElementText(dom.DocumentElement, "state", strState);
        }

        DomUtil.SetElementText(dom.DocumentElement, "refID", Guid.NewGuid().ToString());

        strXml = dom.OuterXml;
        return 0;
    }

    // return:
    //      -1  出错
    //      0   没有发现重复
    //      1   发现重复
    int SearchItemDup(
        string barcode,
        out string strError)
    {
        strError = "";

        LibraryChannel channel = _form.GetChannel();
        try
        {
            long lRet = channel.SearchItem(
    null,
    "<全部>",
    barcode,
    100,
    "册条码号",
    "exact",
    "zh",
    "dup",
    "", // strSearchStyle
    "", // strOutputStyle
    out strError);
            if (lRet == -1)
                return -1;
            return (lRet > 0 ? 1 : 0);
        }
        finally
        {
            _form.ReturnChannel(channel);
        }
    }

    int BuildBarcodeFile(
        string fileName,
        out string strError)
    {
        strError = "";

        this.WriteTextToConsole($"正在创建条码号文件 {fileName}...");

        bool succeed = false;

        LibraryChannel channel = _form.GetChannel();
        try
        {
            long lRet = channel.SearchItem(
    null,
    "<全部>",
    "",
    -1,
    "册条码号",
    "left",
    "zh",
    "download111",
    "", // strSearchStyle
    "", // strOutputStyle
    out strError);
            if (lRet == -1)
                return -1;

            long hitcount = lRet;

            int skip_count = 0;
            int error_count = 0;
            int succeed_count = 0;

            PathUtil.CreateDirIfNeed(Path.GetDirectoryName(fileName));

            using (StreamWriter sw = new StreamWriter(fileName, false, Encoding.UTF8))
            {
                if (hitcount > 0)
                {
                    string strStyle = "id,cols,format:@coldef:*/barcode";

                    // 把超时时间改短一点
                    var timeout0 = channel.Timeout;
                    channel.Timeout = TimeSpan.FromSeconds(20);

                    try
                    {
                        // 获取和存储记录
                        ResultSetLoader loader = new ResultSetLoader(channel,
            null,
            "download111",
            strStyle,
            "zh");
                        // loader.Start = start;

                        long i = 0; // start;
                        foreach (DigitalPlatform.LibraryClient.localhost.Record record in loader)
                        {
                            string barcode = "";
                            if (record.Cols != null && record.Cols.Length >= 1)
                                barcode = record.Cols[0];
                            else
                            {
                                skip_count++;
                                continue;
                            }

                            string item_recpath = record.Path;

                            if (string.IsNullOrEmpty(barcode)
                                || barcode.Contains("_"))
                            {
                                skip_count++;
                                continue;
                            }

                            if ((i % 1000) == 0)
                                this.WriteTextToConsole($"i={i}, path={record.Path},barcode={barcode}");

                            sw.WriteLine(barcode);

                            i++;
                            succeed_count++;
                        }
                    }
                    finally
                    {
                        channel.Timeout = timeout0;
                    }
                }
            }

            succeed = true;
            return 0;
        }
        finally
        {
            _form.ReturnChannel(channel);

            if (succeed == false)
                File.Delete(fileName);
            else
                this.WriteTextToConsole($"条码号文件 {fileName} 创建完成");
        }
    }

    void LoadAllItems(
        string fileName,
        Hashtable table)
    {
        using (var s = new StreamReader(fileName, Encoding.UTF8))
        {
            while (true)
            {
                var line = s.ReadLine();
                if (line == null)
                    break;
                line = line.Trim();
                table[line] = 1;
            }
        }
    }

}

references.xml

<?xml version="1.0" encoding="utf-8"?>
<root>
  <ref>system.dll</ref>
  <ref>system.windows.forms.dll</ref>
  <ref>system.xml.dll</ref>
  <ref>system.web.dll</ref>
</root>

DigitalPlatform avatar Jun 04 '22 14:06 DigitalPlatform

把带有后缀的册条码号的册记录路径导出

在内务书目查询窗运行。算法是:针对一条书目记录的所有册记录,列出册条码号。如果去掉后缀剩下的纯净册条码号在同一种书目记录下的其他册记录里面找到,则说明需要删除,那么就导出到记录路径文件。

delete_dup_item.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;

using dp2Circulation;

using DigitalPlatform;
using DigitalPlatform.Marc;
using DigitalPlatform.Xml;
using DigitalPlatform.Script;
using DigitalPlatform.LibraryClient;
using DigitalPlatform.LibraryClient.localhost;

public class MyMarcQueryHost : MarcQueryHost
{
    StreamWriter _sw = null;

    int _count = 0;

    public override void FreeResources()
    {
        if (_sw != null)
        {
            _sw.Close();
            _sw = null;
        }
    }

    public override void OnBegin(object sender, StatisEventArgs e)
    {
        string strError = "";

        SaveFileDialog dlg = new SaveFileDialog();

        dlg.Title = "请指定要创建的记录路径文件名";
        dlg.CreatePrompt = false;
        dlg.OverwritePrompt = true;
        dlg.FileName = "";
        dlg.InitialDirectory = Environment.CurrentDirectory;
        dlg.Filter = "记录路径文件 (*.txt)|*.txt|All files (*.*)|*.*";

        dlg.RestoreDirectory = true;

        if (dlg.ShowDialog() != DialogResult.OK)
        {
            e.Continue = ContinueType.SkipAll;
            return;
        }

        _sw = new StreamWriter(dlg.FileName, false, Encoding.UTF8);
        return;
    ERROR1:
        e.Continue = ContinueType.Error;
        e.ParamString = strError;
    }

    public override void OnRecord(object sender, StatisEventArgs e)
    {
        string strError = "";

        LibraryChannel channel = this.MainForm.GetChannel();
        try
        {
            SubItemLoader loader = new SubItemLoader();
            loader.BiblioRecPath = this.RecordPath;
            loader.Channel = channel;
            loader.Stop = null;
            loader.DbType = "item";

            loader.Prompt -= new MessagePromptEventHandler(loader_Prompt);
            loader.Prompt += new MessagePromptEventHandler(loader_Prompt);

            // 没有 _ 的册条码号
            List<string> barcodes = new List<string>();
            //
            List<BarcodeAndPath> items = new List<BarcodeAndPath>();
            foreach (EntityInfo info in loader)
            {
                if (info.ErrorCode != ErrorCodeValue.NoError)
                {
                    strError = "路径为 '" + info.OldRecPath + "' 的册记录装载中发生错误: " + info.ErrorInfo;  // NewRecPath
                    goto ERROR1;
                }

                XmlDocument item_dom = new XmlDocument();
                item_dom.LoadXml(info.OldRecord);

                string barcode = DomUtil.GetElementText(item_dom.DocumentElement, "barcode");
                if (barcode.Contains("_") == false)
                    barcodes.Add(barcode);
                else
                    items.Add(new BarcodeAndPath
                    {
                        Barcode = barcode,
                        Path = info.OldRecPath
                    });
            }

            foreach (var item in items)
            {
                string pure_barcode = GetPureBarcode(item.Barcode);
                if (barcodes.IndexOf(pure_barcode) != -1)
                {
                    _sw.WriteLine(item.Path);
                    _count++;
                }
            }

            return;
        }
        finally
        {
            this.MainForm.ReturnChannel(channel);
        }
        return;
    ERROR1:
        e.Continue = ContinueType.Error;
        e.ParamString = strError;
    }

    void loader_Prompt(object sender, MessagePromptEventArgs e)
    {
        // TODO: 不再出现此对话框。不过重试有个次数限制,同一位置失败多次后总要出现对话框才好
        if (e.Actions == "yes,no,cancel")
        {
            DialogResult result = AutoCloseMessageBox.Show(
                this.MainForm,
                e.MessageText + "\r\n\r\n将自动重试操作\r\n\r\n(点右上角关闭按钮可以中断批处理)",
                20 * 1000,
                "delete_dup_item");
            if (result == DialogResult.Cancel)
                e.ResultAction = "no";
            else
                e.ResultAction = "yes";
        }
    }

    static string GetPureBarcode(string barcode)
    {
        if (string.IsNullOrEmpty(barcode))
            return barcode;
        int index = barcode.IndexOf("_");
        if (index == -1)
            return barcode;
        return barcode.Substring(0, index);
    }

    class BarcodeAndPath
    {
        public string Barcode { get; set; }
        public string Path { get; set; }
    }

    public override void OnEnd(object sender, StatisEventArgs e)
    {
        if (_sw != null)
        {
            _sw.Close();
            _sw = null;
        }

        MessageBox.Show(this.MainForm, $"导出记录路径 {_count} 个");
    }

}

DigitalPlatform avatar Jun 04 '22 14:06 DigitalPlatform

修改形态错误的册条码号

(没有实际投入使用)

change_barcode.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;

using dp2Circulation;

using DigitalPlatform.Xml;
using DigitalPlatform.Text;
using DigitalPlatform.LibraryClient;

// 修改册条码号 2022/6/9
/*
... 原始数据的问题。部分我们可以帮忙处理下。数量比较大的,有规律的可以再重新导入dp2系统时处理一下号码,但是少数的,没有规律的系统没有办法处理。只能老师自己根据实物逐条处理。您看下,一共有这几类。

1.  五位数的号码:在前面加2个0,变成7位数。例如:原 ... 中的“64645”导入dp2系统后变成“0064645”。
2.  六位数的号码:原始号码丢失的数字没有规律,直接将原始条码号导入dp2系统,后续老师根据实物修改。
3. “h”+7位纯数字:统一去掉“h”变成7位数,例如:原 ...中的“h0065247”导入dp2系统后变成“0065247”。
4.  “h”+6位纯数字:统一去掉“h”后,直接将原始条码号导入dp2系统,后续老师根据实物修改。
5.  “h”+5位纯数字:统一去掉“h”后,直接将原始条码号导入dp2系统,后续老师根据实物修改。
6.  “h”+8位纯数字:统一去掉“h”后,直接将原始条码号导入dp2系统,后续老师根据实物修改。
7.  8位纯数字号码:是很早之前登记的册条码号,直接导入dp2系统。
8.  其他带字母,特殊符号的号码:直接将原始条码号导入dp2系统,这部分号码属于少数。
* */

public class MyItemHost : ItemHost
{
    // 本批改后的条码号
    Hashtable _table = new Hashtable();

    public override void OnInitial(object sender, StatisEventArgs e)
    {
        if (this.DbType != "item")
        {
            e.Continue = ContinueType.Error;
            e.ParamString = "本程序只能被 册查询窗 所调用";
            return;
        }
    }

    public override void OnRecord(object sender, StatisEventArgs e)
    {
        string barcode = DomUtil.GetElementText(this.ItemDom.DocumentElement, "barcode");
        if (string.IsNullOrEmpty(barcode))
            return;

        // 跳过带有后缀的
        if (barcode.Contains("_"))
            return;

        string strError = "";

        string comment = DomUtil.GetElementText(this.ItemDom.DocumentElement, "comment");
        string new_comment = null;
        string new_barcode = null;

        // 1.  五位数的号码:在前面加2个0,变成7位数。例如:原DT1000中的“64645”导入dp2系统后变成“0064645”。
        if (barcode.Length == 5 && StringUtil.IsPureNumber(barcode))
        {
            new_barcode = barcode.PadLeft(7, '0');
            new_comment = AppendComment(comment, $"册条码号从 '{barcode}' 修改为 '{new_barcode}'");
            goto CHANGE_BARCODE;
        }

        // 2.  六位数的号码:原始号码丢失的数字没有规律,直接将原始条码号导入dp2系统,后续老师根据实物修改。
        if (barcode.Length == 6 && StringUtil.IsPureNumber(barcode))
        {
            new_comment = AppendComment(comment, $"条码号 '{barcode}' (6位)有误,请注意检查修改");
            goto CHANGE_BARCODE;
        }

        if (barcode.Length == 7 && StringUtil.IsPureNumber(barcode))
            return;

        // 7.  8位纯数字号码:是很早之前登记的册条码号,直接导入dp2系统。
        if (barcode.Length == 8 && StringUtil.IsPureNumber(barcode))
            return;

        // A1234567 这样的是正确的号码
        if (barcode.Length == 8 && barcode.StartsWith("A"))
        {
            // 第二字符以后的全部
            string rest = barcode.Substring(1);
            if (StringUtil.IsPureNumber(barcode) == false)
                return;
        }

        {
            new_comment = AppendComment(comment, $"条码号 '{barcode}' 不符合规则,请注意检查修改");
            goto CHANGE_BARCODE;
        }

        return;
    CHANGE_BARCODE:
        if (new_barcode != null)
        {
            // 对修改后的 barcode 进行查重。另外修改后的 barcode 要进入一个 Hashtable,辅助查重
            int nRet = SearchItemDup(
    new_barcode,
    out strError);
            if (nRet == -1)
            {
                goto ERROR1;
            }

            if (_table.ContainsKey(new_barcode))
                nRet++;

            if (nRet > 0)
            {
                if (new_comment == null)
                    new_comment = comment;
                new_comment = AppendComment(new_comment, $"尝试将册条码号从 '{barcode}' 修改为 '{new_barcode}' 时遇到重复,放弃修改");
                this.Changed = true;

                this.OutputText($"尝试将册条码号从 '{barcode}' 修改为 '{new_barcode}' 时遇到重复,放弃修改", 2);
            }
            else
            {
                _table[new_barcode] = 1;
                DomUtil.SetElementText(this.ItemDom.DocumentElement, "barcode", new_barcode);
                this.OutputText($"{this.RecordPath}: {barcode} --> {new_barcode}", 0);
                this.Changed = true;
            }
        }
        if (new_comment != null)
        {
            DomUtil.SetElementText(this.ItemDom.DocumentElement, "comment", new_comment);
            this.OutputText($"{new_comment}", 0);
            this.Changed = true;
        }
        return;
    ERROR1:
        e.Continue = ContinueType.Error;
        e.ParamString = strError;
    }

    public override void OnEnd(object sender, StatisEventArgs e)
    {
        this.RemoveUnchangedUiItems();
    }

    string AppendComment(string old_comment, string text)
    {
        if (string.IsNullOrEmpty(old_comment) == false)
            return old_comment + "; " + text;
        return text;
    }

    // return:
    //      -1  出错
    //      0   没有发现重复
    //      >=1   发现重复
    int SearchItemDup(
        string barcode,
        out string strError)
    {
        strError = "";

        var form = this.MainForm;

        LibraryChannel channel = form.GetChannel();
        try
        {
            long lRet = channel.SearchItem(
    null,
    "<全部>",
    barcode,
    100,
    "册条码号",
    "exact",
    "zh",
    "dup",
    "", // strSearchStyle
    "", // strOutputStyle
    out strError);
            if (lRet == -1)
                return -1;
            return (int)lRet;
        }
        finally
        {
            form.ReturnChannel(channel);
        }
    }
}

DigitalPlatform avatar Jun 20 '22 15:06 DigitalPlatform